From c32fcdf4c959737767bc25d2dc647b945c1ace70 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Fri, 4 Sep 2020 09:10:21 +0200 Subject: [PATCH 01/51] fix typo --- spacy/ml/models/tok2vec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacy/ml/models/tok2vec.py b/spacy/ml/models/tok2vec.py index faa5350d4..b9c9b8766 100644 --- a/spacy/ml/models/tok2vec.py +++ b/spacy/ml/models/tok2vec.py @@ -165,7 +165,7 @@ def MultiHashEmbed( @registry.architectures.register("spacy.CharacterEmbed.v1") def CharacterEmbed(width: int, rows: int, nM: int, nC: int): - """Construct an embedded representations based on character embeddings, using + """Construct an embedded representation based on character embeddings, using a feed-forward network. A fixed number of UTF-8 byte characters are used for each word, taken from the beginning and end of the word equally. Padding is used in the centre for words that are too short. From 40058ee626f8acf91ce86e62c8268759b63c8d5b Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Tue, 8 Sep 2020 12:23:06 +0200 Subject: [PATCH 02/51] Update to latest spacy-lookups-data --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index fc33abedb..bc5b904b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -64,7 +64,7 @@ console_scripts = [options.extras_require] lookups = - spacy_lookups_data>=0.3.2,<0.4.0 + spacy_lookups_data==0.4.0.dev0 cuda = cupy>=5.0.0b4,<9.0.0 cuda80 = From b470062153e52983b1aca661cd2c96c6f38e88d4 Mon Sep 17 00:00:00 2001 From: Matthew Honnibal Date: Tue, 8 Sep 2020 15:23:34 +0200 Subject: [PATCH 03/51] Add CLI registry (#6037) --- spacy/cli/_util.py | 4 +++- spacy/util.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index 0ecb5ad8f..ee83ec75e 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -13,7 +13,7 @@ from thinc.config import Config, ConfigValidationError from configparser import InterpolationError from ..schemas import ProjectConfigSchema, validate -from ..util import import_file, run_command, make_tempdir +from ..util import import_file, run_command, make_tempdir, registry if TYPE_CHECKING: from pathy import Pathy # noqa: F401 @@ -54,6 +54,8 @@ app.add_typer(init_cli) def setup_cli() -> None: + # Make sure the entry-point for CLI runs, so that they get imported. + registry.cli.get_all() # Ensure that the help messages always display the correct prompt command = get_command(app) command(prog_name=COMMAND) diff --git a/spacy/util.py b/spacy/util.py index fa4815df8..bd567ddc7 100644 --- a/spacy/util.py +++ b/spacy/util.py @@ -93,6 +93,7 @@ class registry(thinc.registry): # environment. spaCy models packaged with `spacy package` will "advertise" # themselves via entry points. models = catalogue.create("spacy", "models", entry_points=True) + cli = catalogue.create("spacy", "cli", entry_points=True) class SimpleFrozenDict(dict): From ba5f4c9b32fd2d625974af13be44677dcd75cf32 Mon Sep 17 00:00:00 2001 From: Matthew Honnibal Date: Tue, 8 Sep 2020 15:24:47 +0200 Subject: [PATCH 04/51] Add words and seconds to train info --- spacy/cli/train.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spacy/cli/train.py b/spacy/cli/train.py index 6be47fa39..146d26edb 100644 --- a/spacy/cli/train.py +++ b/spacy/cli/train.py @@ -1,4 +1,5 @@ from typing import Optional, Dict, Any, Tuple, Union, Callable, List +from timeit import default_timer as timer import srsly import tqdm from pathlib import Path @@ -286,9 +287,12 @@ def train_while_improving( ] raw_batches = util.minibatch(raw_examples, size=8) + words_seen = 0 + start_time = timer() for step, (epoch, batch) in enumerate(train_data): dropout = next(dropouts) for subbatch in subdivide_batch(batch, accumulate_gradient): + nlp.update( subbatch, drop=dropout, losses=losses, sgd=False, exclude=exclude ) @@ -317,6 +321,7 @@ def train_while_improving( else: score, other_scores = (None, None) is_best_checkpoint = None + words_seen += sum(len(eg) for eg in batch) info = { "epoch": epoch, "step": step, @@ -324,6 +329,8 @@ def train_while_improving( "other_scores": other_scores, "losses": losses, "checkpoints": results, + "seconds": int(timer() - start_time), + "words": words_seen, } yield batch, info, is_best_checkpoint if is_best_checkpoint is not None: From 5d09e3e15459d4ad0ec25ef7e0b5b3264eb54dc8 Mon Sep 17 00:00:00 2001 From: Matthew Honnibal Date: Tue, 8 Sep 2020 15:25:10 +0200 Subject: [PATCH 05/51] Set version to v3.0.0a15 --- spacy/about.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacy/about.py b/spacy/about.py index 7d0e85a17..9bce6fd35 100644 --- a/spacy/about.py +++ b/spacy/about.py @@ -1,6 +1,6 @@ # fmt: off __title__ = "spacy-nightly" -__version__ = "3.0.0a14" +__version__ = "3.0.0a15" __release__ = True __download_url__ = "https://github.com/explosion/spacy-models/releases/download" __compatibility__ = "https://raw.githubusercontent.com/explosion/spacy-models/master/compatibility.json" From 4b82882767d2de7789b96f3b64cafd9565f52da0 Mon Sep 17 00:00:00 2001 From: Matthew Honnibal Date: Tue, 8 Sep 2020 15:31:21 +0200 Subject: [PATCH 06/51] Fix defaults --- spacy/default_config.cfg | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/spacy/default_config.cfg b/spacy/default_config.cfg index 9507f0f0a..7cd71453f 100644 --- a/spacy/default_config.cfg +++ b/spacy/default_config.cfg @@ -52,7 +52,7 @@ path = ${paths.train} # data is passed in sentence-by-sentence via some prior preprocessing. gold_preproc = false # Limitations on training document length -max_length = 2000 +max_length = 0 # Limitation on number of training examples limit = 0 @@ -64,7 +64,7 @@ path = ${paths.dev} # data is passed in sentence-by-sentence via some prior preprocessing. gold_preproc = false # Limitations on training document length -max_length = 2000 +max_length = 0 # Limitation on number of training examples limit = 0 @@ -88,9 +88,4 @@ L2 = 0.01 grad_clip = 1.0 use_averages = false eps = 1e-8 - -[training.optimizer.learn_rate] -@schedules = "warmup_linear.v1" -warmup_steps = 250 -total_steps = 20000 -initial_rate = 0.001 +learn_rate = 0.001 From bd8f9b188bd423db79da6be60206e6e7f6711871 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Tue, 8 Sep 2020 17:24:36 +0200 Subject: [PATCH 07/51] small fixes --- spacy/ml/models/tok2vec.py | 4 ++-- website/docs/api/architectures.md | 8 ++++---- website/docs/usage/training.md | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/spacy/ml/models/tok2vec.py b/spacy/ml/models/tok2vec.py index b9c9b8766..2e5f8a802 100644 --- a/spacy/ml/models/tok2vec.py +++ b/spacy/ml/models/tok2vec.py @@ -176,8 +176,8 @@ def CharacterEmbed(width: int, rows: int, nM: int, nC: int): ensures that the final character is always in the last position, instead of being in an arbitrary position depending on the word length. - The characters are embedded in a embedding table with 256 rows, and the - vectors concatenated. A hash-embedded vector of the NORM of the word is + The characters are embedded in a embedding table with a given number of rows, + and the vectors concatenated. A hash-embedded vector of the NORM of the word is also concatenated on, and the result is then passed through a feed-forward network to construct a single vector to represent the information. diff --git a/website/docs/api/architectures.md b/website/docs/api/architectures.md index ee844d961..bcca97324 100644 --- a/website/docs/api/architectures.md +++ b/website/docs/api/architectures.md @@ -181,10 +181,10 @@ characters would be `"jumpping"`: 4 from the start, 4 from the end. This ensures that the final character is always in the last position, instead of being in an arbitrary position depending on the word length. -The characters are embedded in a embedding table with 256 rows, and the vectors -concatenated. A hash-embedded vector of the `NORM` of the word is also -concatenated on, and the result is then passed through a feed-forward network to -construct a single vector to represent the information. +The characters are embedded in a embedding table with a given number of rows, +and the vectors concatenated. A hash-embedded vector of the `NORM` of the word +is also concatenated on, and the result is then passed through a feed-forward +network to construct a single vector to represent the information. | Name | Description | | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/website/docs/usage/training.md b/website/docs/usage/training.md index 9c18e4606..3f0141d72 100644 --- a/website/docs/usage/training.md +++ b/website/docs/usage/training.md @@ -683,7 +683,7 @@ You can also implement your own batch size schedule to use during training. The import spacy @spacy.registry.schedules("my_custom_schedule.v1") -def my_custom_schedule(start: int = 1, factor: int = 1.001): +def my_custom_schedule(start: int = 1, factor: float = 1.001): while True: yield start start = start * factor From d0a8849e4d3f2383a9d19d06e94bb7a88644e81b Mon Sep 17 00:00:00 2001 From: svlandeg Date: Tue, 8 Sep 2020 18:32:12 +0200 Subject: [PATCH 08/51] fix typo --- spacy/tokens/doc.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacy/tokens/doc.pyx b/spacy/tokens/doc.pyx index 3f8c735fb..93520aeda 100644 --- a/spacy/tokens/doc.pyx +++ b/spacy/tokens/doc.pyx @@ -576,7 +576,7 @@ cdef class Doc: entity_type = 0 kb_id = 0 - # Set ent_iob to Missing (0) bij default unless this token was nered before + # Set ent_iob to Missing (0) by default unless this token was nered before ent_iob = 0 if self.c[i].ent_iob != 0: ent_iob = 2 From b35a26ea5d07c5da62f48060d123cebb1932e2fd Mon Sep 17 00:00:00 2001 From: svlandeg Date: Tue, 8 Sep 2020 18:32:58 +0200 Subject: [PATCH 09/51] example wrapped Torch model and chaining with Thinc --- website/docs/usage/layers-architectures.md | 59 ++++++++++++++++++++-- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index e24b776c8..eb46dd137 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -118,7 +118,7 @@ code. If no model is specified for the [`TextCategorizer`](/api/textcategorizer), the [TextCatEnsemble](/api/architectures#TextCatEnsemble) architecture is used by -default. This architecture combines a simpel bag-of-words model with a neural +default. This architecture combines a simple bag-of-words model with a neural network, usually resulting in the most accurate results, but at the cost of speed. The config file for this model would look something like this: @@ -225,10 +225,59 @@ you'll be able to try it out in any of the spaCy components. ​ Thinc allows you to [wrap models](https://thinc.ai/docs/usage-frameworks) written in other machine learning frameworks like PyTorch, TensorFlow and MXNet -using a unified [`Model`](https://thinc.ai/docs/api-model) API. As well as -**wrapping whole models**, Thinc lets you call into an external framework for -just **part of your model**: you can have a model where you use PyTorch just for -the transformer layers, using "native" Thinc layers to do fiddly input and +using a unified [`Model`](https://thinc.ai/docs/api-model) API. + +For example, let's use Pytorch to define a very simple Neural network consisting +of two hidden `Linear` layers with `ReLU` activation and dropout, and a +softmax-activated output layer. + +```python +from torch import nn + +torch_model = nn.Sequential( + nn.Linear(width, hidden_width), + nn.ReLU(), + nn.Dropout2d(dropout), + nn.Linear(hidden_width, nO), + nn.ReLU(), + nn.Dropout2d(dropout), + nn.Softmax(dim=1) + ) +``` + +This PyTorch model can be wrapped as a Thinc `Model` by using Thinc's `PyTorchWrapper`: + +```python +from thinc.api import PyTorchWrapper + +wrapped_pt_model = PyTorchWrapper(torch_model) +``` + +The resulting wrapped `Model` can be used as a **custom architecture** as such, or +can be a **subcomponent of a larger model**. For instance, we can use Thinc's +[`chain`](https://thinc.ai/docs/api-layers#chain) +combinator, which works like `Sequential` in PyTorch, +to combine the wrapped model with other components in a larger network. +This effectively means that you can easily wrap different components +from different frameworks, and "glue" them together with Thinc: + +```python +from thinc.api import chain, with_array +from spacy.ml import CharacterEmbed + +embed = CharacterEmbed(width, embed_size, nM, nC) +model = chain(embed, with_array(wrapped_pt_model)) +``` + +In the above example, we have combined our custom PyTorch model with a +character embedding layer defined by spaCy. +[CharacterEmbed](/api/architectures#CharacterEmbed) returns a +`Model` that takes a `List[Doc]` as input, and outputs a `List[Floats2d]`. +To make sure that the wrapped Pytorch model receives valid inputs, we use Thinc's +[`with_array`](https://thinc.ai/docs/api-layers#with_array) helper. + +As another example, you could have a model where you use PyTorch just for +the transformer layers, and use "native" Thinc layers to do fiddly input and output transformations and add on task-specific "heads", as efficiency is less of a consideration for those parts of the network. From 1c476b4b41dc25fd642865b580c42bf936e46c2b Mon Sep 17 00:00:00 2001 From: svlandeg Date: Tue, 8 Sep 2020 20:22:20 +0200 Subject: [PATCH 10/51] how to register and use custom function --- website/docs/usage/layers-architectures.md | 66 ++++++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index eb46dd137..f9f3e81a0 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -281,14 +281,70 @@ the transformer layers, and use "native" Thinc layers to do fiddly input and output transformations and add on task-specific "heads", as efficiency is less of a consideration for those parts of the network. - - -## Implementing models in Thinc {#thinc} - - ## Models for trainable components {#components} +To use our custom model including the Pytorch subnetwork, all we need to do is register +the architecture. The full example then becomes: + +```python +from typing import List +from thinc.types import Floats2d +from thinc.api import Model, PyTorchWrapper, chain, with_array +import spacy +from spacy.tokens.doc import Doc +from spacy.ml import CharacterEmbed +from torch import nn + +@spacy.registry.architectures("CustomTorchModel.v1") +def TorchModel(nO: int, + width: int, + hidden_width: int, + embed_size: int, + nM: int, + nC: int, + dropout: float, +) -> Model[List[Doc], List[Floats2d]]: + embed = CharacterEmbed(width, embed_size, nM, nC) + torch_model = nn.Sequential( + nn.Linear(width, hidden_width), + nn.ReLU(), + nn.Dropout2d(dropout), + nn.Linear(hidden_width, nO), + nn.ReLU(), + nn.Dropout2d(dropout), + nn.Softmax(dim=1) + ) + wrapped_pt_model = PyTorchWrapper(torch_model) + model = chain(embed, with_array(wrapped_pt_model)) + return model +``` + +Now you can use this model definition in any existing trainable spaCy component, +by specifying it in the config file: + +```ini +### config.cfg (excerpt) {highlight="6-6"} +[components.tagger] +factory = "tagger" + +[components.tagger.model] +@architectures = "CustomTorchModel.v1" +nO = 50 +nM = 64 +nC = 8 +dropout = 0.2 +width = 96 +hidden_width = 48 +embed_size = 2000 +``` + +In this configuration, we pass all required parameters for the various +subcomponents of the custom architecture as settings in the training config file. +Remember that it is best not to rely on any (hidden) default values, to ensure that +training configs are complete and experiments fully reproducible. + + + + +## Create new components {#components} + ```python ### scripts/visualize.py import spacy_streamlit diff --git a/website/docs/usage/training.md b/website/docs/usage/training.md index 9c18e4606..3f0141d72 100644 --- a/website/docs/usage/training.md +++ b/website/docs/usage/training.md @@ -683,7 +683,7 @@ You can also implement your own batch size schedule to use during training. The import spacy @spacy.registry.schedules("my_custom_schedule.v1") -def my_custom_schedule(start: int = 1, factor: int = 1.001): +def my_custom_schedule(start: int = 1, factor: float = 1.001): while True: yield start start = start * factor From e39242c4e6814a3879a3e04266105b5a4ca83cba Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 9 Sep 2020 11:25:35 +0200 Subject: [PATCH 15/51] formatting --- website/docs/usage/layers-architectures.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index 51f13591d..75bafab4d 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -297,7 +297,8 @@ from spacy.ml import CharacterEmbed from torch import nn @spacy.registry.architectures("CustomTorchModel.v1") -def TorchModel(nO: int, +def TorchModel( + nO: int, width: int, hidden_width: int, embed_size: int, @@ -324,19 +325,19 @@ Now you can use this model definition in any existing trainable spaCy component, by specifying it in the config file: ```ini -### config.cfg (excerpt) {highlight="6-6"} +### config.cfg (excerpt) {highlight="5-5"} [components.tagger] factory = "tagger" [components.tagger.model] @architectures = "CustomTorchModel.v1" nO = 50 -nM = 64 -nC = 8 -dropout = 0.2 width = 96 hidden_width = 48 embed_size = 2000 +nM = 64 +nC = 8 +dropout = 0.2 ``` In this configuration, we pass all required parameters for the various From 4c080b3a98017aeb992e739a5626e34d547a8f71 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 9 Sep 2020 13:57:05 +0200 Subject: [PATCH 16/51] details on Thinc shape inference --- website/docs/usage/layers-architectures.md | 75 ++++++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index 75bafab4d..db1abda46 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -348,13 +348,14 @@ ensure that training configs are complete and experiments fully reproducible. ## Thinc implemention details {#thinc} Ofcourse it's also possible to define the `Model` from the previous section -entirely in Thinc. The Thinc documentation documents the +entirely in Thinc. The Thinc documentation provides details on the [various layers](https://thinc.ai/docs/api-layers) and helper functions available. The combinators often used in Thinc can be used to [overload operators](https://thinc.ai/docs/usage-models#operators). A common -usage is for example to bind `chain` to `>>`: +usage is to bind `chain` to `>>`. The "native" Thinc version of our simple +neural network would then become: ```python from thinc.api import chain, with_array, Model, Relu, Dropout, Softmax @@ -364,11 +365,11 @@ char_embed = CharacterEmbed(width, embed_size, nM, nC) with Model.define_operators({">>": chain}): layers = ( - Relu(nO=hidden_width, nI=width) + Relu(hidden_width, width) >> Dropout(dropout) - >> Relu(nO=hidden_width, nI=hidden_width) + >> Relu(hidden_width, hidden_width) >> Dropout(dropout) - >> Softmax(nO=nO, nI=hidden_width) + >> Softmax(nO, hidden_width) ) model = char_embed >> with_array(layers) ``` @@ -378,16 +379,76 @@ argument, followed (optionally) by the input dimension (`nI`). This is in contrast to how the PyTorch layers are defined, where `in_features` precedes `out_features`.** +### Shape inference in thinc {#shape-inference} - +It is not strictly necessary to define all the input and output dimensions for +each layer, as Thinc can perform shape inference between sequential layers by +matching up the output dimensionality of one layer to the input dimensionality +of the next. This means that we can simplify the `layers` definition: +```python +with Model.define_operators({">>": chain}): + layers = ( + Relu(hidden_width, width) + >> Dropout(dropout) + >> Relu(hidden_width) + >> Dropout(dropout) + >> Softmax(nO) + ) +``` + +Thinc can go one step further and deduce the correct input dimension of the +first layer, and output dimension of the last. To enable this functionality, you +can call [`model.initialize`](https://thinc.ai/docs/api-model#initialize) with +an input sample `X` and an output sample `Y` with the correct dimensions. + +```python +with Model.define_operators({">>": chain}): + layers = ( + Relu(hidden_width) + >> Dropout(dropout) + >> Relu(hidden_width) + >> Dropout(dropout) + >> Softmax() + ) + model = char_embed >> with_array(layers) + model.initialize(X=input_sample, Y=output_sample) +``` + +The built-in +[pipeline components](http://localhost:8000/usage/processing-pipelines) in spaCy +ensure that their internal models are always initialized with appropriate sample +data. In this case, `X` is typically a `List` of `Doc` objects, while `Y` is a +`List` of 1D or 2D arrays, depending on the specific task. + +### Dropout and normalization {#drop-norm} + +Many of the `Thinc` layers allow you to define a `dropout` argument that will +result in "chaining" an additional +[`Dropout`](https://thinc.ai/docs/api-layers#dropout) layer. Optionally, you can +often specify whether or not you want to add layer normalization, which would +result in an additional +[`LayerNorm`](https://thinc.ai/docs/api-layers#layernorm) layer. + +That means that the following `layers` definition is equivalent to the previous: + +```python +with Model.define_operators({">>": chain}): + layers = ( + Relu(hidden_width, dropout=dropout, normalize=False) + >> Relu(hidden_width, dropout=dropout, normalize=False) + >> Softmax() + ) + model = char_embed >> with_array(layers) + model.initialize(X=input_sample, Y=output_sample) +``` ## Create new components {#components} ![Diagram of a pipeline component with its model](../images/layers-architectures.svg) diff --git a/website/docs/usage/processing-pipelines.md b/website/docs/usage/processing-pipelines.md index a875df29c..3d756215f 100644 --- a/website/docs/usage/processing-pipelines.md +++ b/website/docs/usage/processing-pipelines.md @@ -1028,11 +1028,11 @@ plug fully custom machine learning components into your pipeline. You'll need the following: 1. **Model:** A Thinc [`Model`](https://thinc.ai/docs/api-model) instance. This - can be a model using [layers](https://thinc.ai/docs/api-layers) implemented - in Thinc, or a [wrapped model](https://thinc.ai/docs/usage-frameworks) - implemented in PyTorch, TensorFlow, MXNet or a fully custom solution. The - model must take a list of [`Doc`](/api/doc) objects as input and can have any - type of output. + can be a model using implemented in + [Thinc](/usage/layers-architectures#thinc), or a + [wrapped model](/usage/layers-architectures#frameworks) implemented in + PyTorch, TensorFlow, MXNet or a fully custom solution. The model must take a + list of [`Doc`](/api/doc) objects as input and can have any type of output. 2. **Pipe subclass:** A subclass of [`Pipe`](/api/pipe) that implements at least two methods: [`Pipe.predict`](/api/pipe#predict) and [`Pipe.set_annotations`](/api/pipe#set_annotations). @@ -1078,8 +1078,9 @@ _first_ create a `Model` from a [registered architecture](/api/architectures), validate its arguments and _then_ pass the object forward to the component. This means that the config can express very complex, nested trees of objects – but the objects don't have to pass the model settings all the way down to the -components. It also makes the components more **modular** and lets you swap -different architectures in your config, and re-use model definitions. +components. It also makes the components more **modular** and lets you +[swap](/usage/layers-architectures#swap-architectures) different architectures +in your config, and re-use model definitions. ```ini ### config.cfg (excerpt) @@ -1134,7 +1135,7 @@ loss is calculated and to add evaluation scores to the training output. For more details on how to implement your own trainable components and model architectures, and plug existing models implemented in PyTorch or TensorFlow into your spaCy pipeline, see the usage guide on -[layers and model architectures](/usage/layers-architectures#components). +[layers and model architectures](/usage/layers-architectures). From a8aa9a806818955d3bbecd0413878d6892ff8002 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 9 Sep 2020 15:56:27 +0200 Subject: [PATCH 18/51] document Pipe API details, crossreferences etc --- website/docs/api/language.md | 9 ++- website/docs/api/pipe.md | 86 ++++++++++++++++++++-- website/docs/usage/layers-architectures.md | 12 +-- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/website/docs/api/language.md b/website/docs/api/language.md index 7799f103b..9c9ccb6cf 100644 --- a/website/docs/api/language.md +++ b/website/docs/api/language.md @@ -205,9 +205,16 @@ examples can either be the full training data or a representative sample. They are used to **initialize the models** of trainable pipeline components and are passed each component's [`begin_training`](/api/pipe#begin_training) method, if available. Initialization includes validating the network, -[inferring missing shapes](https://thinc.ai/docs/usage-models#validation) and +[inferring missing shapes](/usage/layers-architectures#shape-inference) and setting up the label scheme based on the data. +If no `get_examples` function is provided when calling `nlp.begin_training`, the +pipeline components will be initialized with generic data. In this case, it is +crucial that the output dimension of each component has already been defined +either in the [config](/usage/training#config), or by calling +[`pipe.add_label`](/api/pipe#add_label) for each possible output label (e.g. for +the tagger or textcat). + The `Language.update` method now takes a **function** that is called with no diff --git a/website/docs/api/pipe.md b/website/docs/api/pipe.md index 57b2af44d..7b77141fa 100644 --- a/website/docs/api/pipe.md +++ b/website/docs/api/pipe.md @@ -286,9 +286,6 @@ context, the original parameters are restored. ## Pipe.add_label {#add_label tag="method"} -Add a new label to the pipe. It's possible to extend trained models with new -labels, but care should be taken to avoid the "catastrophic forgetting" problem. - > #### Example > > ```python @@ -296,10 +293,85 @@ labels, but care should be taken to avoid the "catastrophic forgetting" problem. > pipe.add_label("MY_LABEL") > ``` -| Name | Description | -| ----------- | ----------------------------------------------------------- | -| `label` | The label to add. ~~str~~ | -| **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | + + +This method needs to be overwritten with your own custom `add_label` method. + + + +Add a new label to the pipe, to be predicted by the model. The actual +implementation depends on the specific component, but in general `add_label` +shouldn't be called if the output dimension is already set, or if the model has +already been fully [initialized](#begin_training). If these conditions are +violated, the function will raise an Error. The exception to this rule is when +the component is [resizable](#is_resizable), in which case +[`set_output`](#set_output) should be called to ensure that the model is +properly resized. + +| Name | Description | +| ----------- | ------------------------------------------------------- | +| `label` | The label to add. ~~str~~ | +| **RETURNS** | 0 if the label is already present, otherwise 1. ~~int~~ | + +Note that in general, you don't have to call `pipe.add_label` if you provide a +representative data sample to the [`begin_training`](#begin_training) method. In +this case, all labels found in the sample will be automatically added to the +model, and the output dimension will be +[inferred](/usage/layers-architectures#shape-inference) automatically. + +## Pipe.is_resizable {#is_resizable tag="method"} + +> #### Example +> +> ```python +> can_resize = pipe.is_resizable() +> ``` + +Check whether or not the output dimension of the component's model can be +resized. If this method returns `True`, [`set_output`](#set_output) can be +called to change the model's output dimension. + +| Name | Description | +| ----------- | ---------------------------------------------------------------------------------------------- | +| **RETURNS** | Whether or not the output dimension of the model can be changed after initialization. ~~bool~~ | + +> #### Example +> +> ```python +> def custom_resize(model, new_nO): +> # adjust model +> return model +> custom_model.attrs["resize_output"] = custom_resize +> ``` + +For built-in components that are not resizable, you have to create and train a +new model from scratch with the appropriate architecture and output dimension. + +For custom components, you can implement a `resize_output` function and add it +as an attribute to the component's model. + +## Pipe.set_output {#set_output tag="method"} + +Change the output dimension of the component's model. If the component is not +[resizable](#is_resizable), this method will throw a `NotImplementedError`. + +If a component is resizable, the model's attribute `resize_output` will be +called. This is a function that takes the original model and the new output +dimension `nO`, and changes the model in place. + +When resizing an already trained model, care should be taken to avoid the +"catastrophic forgetting" problem. + +> #### Example +> +> ```python +> if pipe.is_resizable(): +> pipe.set_output(512) +> ``` + +| Name | Description | +| ---- | --------------------------------- | +| `nO` | The new output dimension. ~~int~~ | ## Pipe.to_disk {#to_disk tag="method"} diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index 1e39ffb9a..95afe3239 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -382,9 +382,11 @@ contrast to how the PyTorch layers are defined, where `in_features` precedes ### Shape inference in thinc {#shape-inference} It is not strictly necessary to define all the input and output dimensions for -each layer, as Thinc can perform shape inference between sequential layers by -matching up the output dimensionality of one layer to the input dimensionality -of the next. This means that we can simplify the `layers` definition: +each layer, as Thinc can perform +[shape inference](https://thinc.ai/docs/usage-models#validation) between +sequential layers by matching up the output dimensionality of one layer to the +input dimensionality of the next. This means that we can simplify the `layers` +definition: ```python with Model.define_operators({">>": chain}): @@ -399,8 +401,8 @@ with Model.define_operators({">>": chain}): Thinc can go one step further and deduce the correct input dimension of the first layer, and output dimension of the last. To enable this functionality, you -can call [`model.initialize`](https://thinc.ai/docs/api-model#initialize) with -an input sample `X` and an output sample `Y` with the correct dimensions. +have to call [`model.initialize`](https://thinc.ai/docs/api-model#initialize) +with an input sample `X` and an output sample `Y` with the correct dimensions. ```python with Model.define_operators({">>": chain}): From cb66ea740053e355e6303c75993fc44b5187a509 Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Wed, 9 Sep 2020 16:11:27 +0200 Subject: [PATCH 19/51] Remove simple_ner code (#6041) * remove simple_ner code * remove unused _biluo and _iob files --- .../tok2vec-ner/multihashembed_tok2vec.cfg | 7 +- spacy/ml/_biluo.py | 105 --------- spacy/ml/_iob.py | 88 ------- spacy/ml/models/__init__.py | 1 - spacy/ml/models/simple_ner.py | 104 -------- spacy/pipeline/__init__.py | 2 - spacy/pipeline/simple_ner.py | 223 ------------------ spacy/tests/pipeline/test_simple_ner.py | 106 --------- website/docs/api/architectures.md | 56 ----- 9 files changed, 5 insertions(+), 687 deletions(-) delete mode 100644 spacy/ml/_biluo.py delete mode 100644 spacy/ml/_iob.py delete mode 100644 spacy/ml/models/simple_ner.py delete mode 100644 spacy/pipeline/simple_ner.py delete mode 100644 spacy/tests/pipeline/test_simple_ner.py diff --git a/extra/experiments/tok2vec-ner/multihashembed_tok2vec.cfg b/extra/experiments/tok2vec-ner/multihashembed_tok2vec.cfg index a5fa32b18..e2ab148c6 100644 --- a/extra/experiments/tok2vec-ner/multihashembed_tok2vec.cfg +++ b/extra/experiments/tok2vec-ner/multihashembed_tok2vec.cfg @@ -31,10 +31,13 @@ lang = "en" vectors = null [nlp.pipeline.ner] -factory = "simple_ner" +factory = "ner" [nlp.pipeline.ner.model] -@architectures = "spacy.BiluoTagger.v1" +@architectures = "spacy.TransitionBasedParser.v1" +nr_feature_tokens = 6 +hidden_width = 64 +maxout_pieces = 2 [nlp.pipeline.ner.model.tok2vec] @architectures = "spacy.HashEmbedCNN.v1" diff --git a/spacy/ml/_biluo.py b/spacy/ml/_biluo.py deleted file mode 100644 index 5a66a35bd..000000000 --- a/spacy/ml/_biluo.py +++ /dev/null @@ -1,105 +0,0 @@ -"""Thinc layer to do simpler transition-based parsing, NER, etc.""" -from typing import Dict, Optional -import numpy -from thinc.api import Model -from thinc.types import Padded, Floats3d - - -def BILUO() -> Model[Padded, Padded]: - return Model( - "biluo", - forward, - init=init, - dims={"nO": None}, - attrs={"get_num_actions": get_num_actions}, - ) - - -def init(model, X: Optional[Padded] = None, Y: Optional[Padded] = None): - if X is not None and Y is not None: - if X.data.shape != Y.data.shape: - # TODO: Fix error - raise ValueError("Mismatched shapes (TODO: Fix message)") - model.set_dim("nO", X.data.shape[2]) - elif X is not None: - model.set_dim("nO", X.data.shape[2]) - elif Y is not None: - model.set_dim("nO", Y.data.shape[2]) - elif model.get_dim("nO") is None: - raise ValueError("Dimension unset for BILUO: nO") - - -def forward(model: Model[Padded, Padded], Xp: Padded, is_train: bool): - n_labels = (model.get_dim("nO") - 1) // 4 - n_tokens, n_docs, n_actions = Xp.data.shape - # At each timestep, we make a validity mask of shape (n_docs, n_actions) - # to indicate which actions are valid next for each sequence. To construct - # the mask, we have a state of shape (2, n_actions) and a validity table of - # shape (2, n_actions+1, n_actions). The first dimension of the state indicates - # whether it's the last token, the second dimension indicates the previous - # action, plus a special 'null action' for the first entry. - valid_transitions = model.ops.asarray(_get_transition_table(n_labels)) - prev_actions = model.ops.alloc1i(n_docs) - # Initialize as though prev action was O - prev_actions.fill(n_actions - 1) - Y = model.ops.alloc3f(*Xp.data.shape) - masks = model.ops.alloc3f(*Y.shape) - max_value = Xp.data.max() - for t in range(Xp.data.shape[0]): - is_last = (Xp.lengths < (t + 2)).astype("i") - masks[t] = valid_transitions[is_last, prev_actions] - # Don't train the out-of-bounds sequences. - masks[t, Xp.size_at_t[t] :] = 0 - # Valid actions get 0*10e8, invalid get large negative value - Y[t] = Xp.data[t] + ((masks[t] - 1) * max_value * 10) - prev_actions = Y[t].argmax(axis=-1) - - def backprop_biluo(dY: Padded) -> Padded: - dY.data *= masks - return dY - - return Padded(Y, Xp.size_at_t, Xp.lengths, Xp.indices), backprop_biluo - - -def get_num_actions(n_labels: int) -> int: - # One BEGIN action per label - # One IN action per label - # One LAST action per label - # One UNIT action per label - # One OUT action - return n_labels + n_labels + n_labels + n_labels + 1 - - -def _get_transition_table( - n_labels: int, *, _cache: Dict[int, Floats3d] = {} -) -> Floats3d: - n_actions = get_num_actions(n_labels) - if n_actions in _cache: - return _cache[n_actions] - table = numpy.zeros((2, n_actions, n_actions), dtype="f") - B_start, B_end = (0, n_labels) - I_start, I_end = (B_end, B_end + n_labels) - L_start, L_end = (I_end, I_end + n_labels) - U_start, _ = (L_end, L_end + n_labels) # noqa: F841 - # Using ranges allows us to set specific cells, which is necessary to express - # that only actions of the same label are valid continuations. - B_range = numpy.arange(B_start, B_end) - I_range = numpy.arange(I_start, I_end) - L_range = numpy.arange(L_start, L_end) - # If this is the last token and the previous action was B or I, only L - # of that label is valid - table[1, B_range, L_range] = 1 - table[1, I_range, L_range] = 1 - # If this isn't the last token and the previous action was B or I, only I or - # L of that label are valid. - table[0, B_range, I_range] = 1 - table[0, B_range, L_range] = 1 - table[0, I_range, I_range] = 1 - table[0, I_range, L_range] = 1 - # If this isn't the last token and the previous was L, U or O, B is valid - table[0, L_start:, :B_end] = 1 - # Regardless of whether this is the last token, if the previous action was - # {L, U, O}, U and O are valid. - table[:, L_start:, U_start:] = 1 - _cache[n_actions] = table - return table diff --git a/spacy/ml/_iob.py b/spacy/ml/_iob.py deleted file mode 100644 index 2e6b2ffab..000000000 --- a/spacy/ml/_iob.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Thinc layer to do simpler transition-based parsing, NER, etc.""" -from typing import Dict, Optional -from thinc.api import Ops, Model -from thinc.types import Padded, Floats3d - - -def IOB() -> Model[Padded, Padded]: - return Model( - "biluo", - forward, - init=init, - dims={"nO": None}, - attrs={"get_num_actions": get_num_actions}, - ) - - -def init(model: Model, X: Optional[Padded] = None, Y: Optional[Padded] = None) -> None: - if X is not None and Y is not None: - if X.data.shape != Y.data.shape: - # TODO: Fix error - raise ValueError("Mismatched shapes (TODO: Fix message)") - model.set_dim("nO", X.data.shape[2]) - elif X is not None: - model.set_dim("nO", X.data.shape[2]) - elif Y is not None: - model.set_dim("nO", Y.data.shape[2]) - elif model.get_dim("nO") is None: - raise ValueError("Dimension unset for BILUO: nO") - - -def forward(model: Model[Padded, Padded], Xp: Padded, is_train: bool): - n_labels = (model.get_dim("nO") - 1) // 2 - n_tokens, n_docs, n_actions = Xp.data.shape - # At each timestep, we make a validity mask of shape (n_docs, n_actions) - # to indicate which actions are valid next for each sequence. To construct - # the mask, we have a state of shape (2, n_actions) and a validity table of - # shape (2, n_actions+1, n_actions). The first dimension of the state indicates - # whether it's the last token, the second dimension indicates the previous - # action, plus a special 'null action' for the first entry. - valid_transitions = _get_transition_table(model.ops, n_labels) - prev_actions = model.ops.alloc1i(n_docs) - # Initialize as though prev action was O - prev_actions.fill(n_actions - 1) - Y = model.ops.alloc3f(*Xp.data.shape) - masks = model.ops.alloc3f(*Y.shape) - for t in range(Xp.data.shape[0]): - masks[t] = valid_transitions[prev_actions] - # Don't train the out-of-bounds sequences. - masks[t, Xp.size_at_t[t] :] = 0 - # Valid actions get 0*10e8, invalid get -1*10e8 - Y[t] = Xp.data[t] + ((masks[t] - 1) * 10e8) - prev_actions = Y[t].argmax(axis=-1) - - def backprop_biluo(dY: Padded) -> Padded: - # Masking the gradient seems to do poorly here. But why? - # dY.data *= masks - return dY - - return Padded(Y, Xp.size_at_t, Xp.lengths, Xp.indices), backprop_biluo - - -def get_num_actions(n_labels: int) -> int: - # One BEGIN action per label - # One IN action per label - # One OUT action - return n_labels * 2 + 1 - - -def _get_transition_table( - ops: Ops, n_labels: int, _cache: Dict[int, Floats3d] = {} -) -> Floats3d: - n_actions = get_num_actions(n_labels) - if n_actions in _cache: - return ops.asarray(_cache[n_actions]) - table = ops.alloc2f(n_actions, n_actions) - B_start, B_end = (0, n_labels) - I_start, I_end = (B_end, B_end + n_labels) - O_action = I_end - B_range = ops.xp.arange(B_start, B_end) - I_range = ops.xp.arange(I_start, I_end) - # B and O are always valid - table[:, B_start:B_end] = 1 - table[:, O_action] = 1 - # I can only follow a matching B - table[B_range, I_range] = 1 - - _cache[n_actions] = table - return table diff --git a/spacy/ml/models/__init__.py b/spacy/ml/models/__init__.py index dd58dab00..67e70421f 100644 --- a/spacy/ml/models/__init__.py +++ b/spacy/ml/models/__init__.py @@ -1,6 +1,5 @@ from .entity_linker import * # noqa from .parser import * # noqa -from .simple_ner import * # noqa from .tagger import * # noqa from .textcat import * # noqa from .tok2vec import * # noqa diff --git a/spacy/ml/models/simple_ner.py b/spacy/ml/models/simple_ner.py deleted file mode 100644 index b47e7f349..000000000 --- a/spacy/ml/models/simple_ner.py +++ /dev/null @@ -1,104 +0,0 @@ -from typing import List -from thinc.api import Model, Linear, with_array, softmax_activation, padded2list -from thinc.api import chain, list2padded, configure_normal_init -from thinc.api import Dropout -from thinc.types import Floats2d - -from ...tokens import Doc -from .._biluo import BILUO -from .._iob import IOB -from ...util import registry - - -@registry.architectures.register("spacy.BILUOTagger.v1") -def BiluoTagger( - tok2vec: Model[List[Doc], List[Floats2d]] -) -> Model[List[Doc], List[Floats2d]]: - """Construct a simple NER tagger, that predicts BILUO tag scores for each - token and uses greedy decoding with transition-constraints to return a valid - BILUO tag sequence. - - A BILUO tag sequence encodes a sequence of non-overlapping labelled spans - into tags assigned to each token. The first token of a span is given the - tag B-LABEL, the last token of the span is given the tag L-LABEL, and tokens - within the span are given the tag I-LABEL. Single-token spans are given - the tag U-LABEL. All other tokens are assigned the tag O. - - The BILUO tag scheme generally results in better linear separation between - classes, especially for non-CRF models, because there are more distinct classes - for the different situations (Ratinov et al., 2009). - """ - biluo = BILUO() - linear = Linear( - nO=None, nI=tok2vec.get_dim("nO"), init_W=configure_normal_init(mean=0.02) - ) - model = chain( - tok2vec, - list2padded(), - with_array(chain(Dropout(0.1), linear)), - biluo, - with_array(softmax_activation()), - padded2list(), - ) - return Model( - "biluo-tagger", - forward, - init=init, - layers=[model, linear], - refs={"tok2vec": tok2vec, "linear": linear, "biluo": biluo}, - dims={"nO": None}, - attrs={"get_num_actions": biluo.attrs["get_num_actions"]}, - ) - - -@registry.architectures.register("spacy.IOBTagger.v1") -def IOBTagger( - tok2vec: Model[List[Doc], List[Floats2d]] -) -> Model[List[Doc], List[Floats2d]]: - """Construct a simple NER tagger, that predicts IOB tag scores for each - token and uses greedy decoding with transition-constraints to return a valid - IOB tag sequence. - - An IOB tag sequence encodes a sequence of non-overlapping labelled spans - into tags assigned to each token. The first token of a span is given the - tag B-LABEL, and subsequent tokens are given the tag I-LABEL. - All other tokens are assigned the tag O. - """ - biluo = IOB() - linear = Linear(nO=None, nI=tok2vec.get_dim("nO")) - model = chain( - tok2vec, - list2padded(), - with_array(linear), - biluo, - with_array(softmax_activation()), - padded2list(), - ) - return Model( - "iob-tagger", - forward, - init=init, - layers=[model], - refs={"tok2vec": tok2vec, "linear": linear, "biluo": biluo}, - dims={"nO": None}, - attrs={"get_num_actions": biluo.attrs["get_num_actions"]}, - ) - - -def init(model: Model[List[Doc], List[Floats2d]], X=None, Y=None) -> None: - if model.has_dim("nO") is None and Y: - model.set_dim("nO", Y[0].shape[1]) - nO = model.get_dim("nO") - biluo = model.get_ref("biluo") - linear = model.get_ref("linear") - biluo.set_dim("nO", nO) - if linear.has_dim("nO") is None: - linear.set_dim("nO", nO) - model.layers[0].initialize(X=X, Y=Y) - - -def forward(model: Model, X: List[Doc], is_train: bool): - return model.layers[0](X, is_train) - - -__all__ = ["BiluoTagger"] diff --git a/spacy/pipeline/__init__.py b/spacy/pipeline/__init__.py index 793aa83c3..656182088 100644 --- a/spacy/pipeline/__init__.py +++ b/spacy/pipeline/__init__.py @@ -8,7 +8,6 @@ from .morphologizer import Morphologizer from .pipe import Pipe from .senter import SentenceRecognizer from .sentencizer import Sentencizer -from .simple_ner import SimpleNER from .tagger import Tagger from .textcat import TextCategorizer from .tok2vec import Tok2Vec @@ -25,7 +24,6 @@ __all__ = [ "Pipe", "SentenceRecognizer", "Sentencizer", - "SimpleNER", "Tagger", "TextCategorizer", "Tok2Vec", diff --git a/spacy/pipeline/simple_ner.py b/spacy/pipeline/simple_ner.py deleted file mode 100644 index 951d89931..000000000 --- a/spacy/pipeline/simple_ner.py +++ /dev/null @@ -1,223 +0,0 @@ -from typing import List, Iterable, Optional, Dict, Tuple, Callable, Set -from thinc.types import Floats2d -from thinc.api import SequenceCategoricalCrossentropy, set_dropout_rate, Model -from thinc.api import Optimizer, Config -from thinc.util import to_numpy -from itertools import islice - -from ..errors import Errors -from ..training import Example, spans_from_biluo_tags, iob_to_biluo, biluo_to_iob -from ..training import validate_examples -from ..tokens import Doc -from ..language import Language -from ..vocab import Vocab -from ..scorer import Scorer -from .pipe import Pipe - - -default_model_config = """ -[model] -@architectures = "spacy.BILUOTagger.v1" - -[model.tok2vec] -@architectures = "spacy.HashEmbedCNN.v1" -pretrained_vectors = null -width = 128 -depth = 4 -embed_size = 7000 -window_size = 1 -maxout_pieces = 3 -subword_features = true -""" -DEFAULT_SIMPLE_NER_MODEL = Config().from_str(default_model_config)["model"] - - -@Language.factory( - "simple_ner", - assigns=["doc.ents"], - default_config={"labels": [], "model": DEFAULT_SIMPLE_NER_MODEL}, - scores=["ents_p", "ents_r", "ents_f", "ents_per_type"], - default_score_weights={"ents_f": 1.0, "ents_p": 0.0, "ents_r": 0.0}, -) -def make_simple_ner( - nlp: Language, name: str, model: Model, labels: Iterable[str] -) -> "SimpleNER": - return SimpleNER(nlp.vocab, model, name, labels=labels) - - -class SimpleNER(Pipe): - """Named entity recognition with a tagging model. The model should include - validity constraints to ensure that only valid tag sequences are returned.""" - - def __init__( - self, - vocab: Vocab, - model: Model, - name: str = "simple_ner", - *, - labels: Iterable[str], - ) -> None: - self.vocab = vocab - self.model = model - self.name = name - self.cfg = {"labels": []} - for label in labels: - self.add_label(label) - self.loss_func = SequenceCategoricalCrossentropy( - names=self.get_tag_names(), normalize=True, missing_value=None - ) - assert self.model is not None - - @property - def is_biluo(self) -> bool: - return self.model.name.startswith("biluo") - - @property - def labels(self) -> Tuple[str]: - return tuple(self.cfg["labels"]) - - def add_label(self, label: str) -> None: - """Add a new label to the pipe. - label (str): The label to add. - DOCS: https://nightly.spacy.io/api/simplener#add_label - """ - if not isinstance(label, str): - raise ValueError(Errors.E187) - if label not in self.labels: - self.cfg["labels"].append(label) - self.vocab.strings.add(label) - - def get_tag_names(self) -> List[str]: - if self.is_biluo: - return ( - [f"B-{label}" for label in self.labels] - + [f"I-{label}" for label in self.labels] - + [f"L-{label}" for label in self.labels] - + [f"U-{label}" for label in self.labels] - + ["O"] - ) - else: - return ( - [f"B-{label}" for label in self.labels] - + [f"I-{label}" for label in self.labels] - + ["O"] - ) - - def predict(self, docs: List[Doc]) -> List[Floats2d]: - scores = self.model.predict(docs) - return scores - - def set_annotations(self, docs: List[Doc], scores: List[Floats2d]) -> None: - """Set entities on a batch of documents from a batch of scores.""" - tag_names = self.get_tag_names() - for i, doc in enumerate(docs): - actions = to_numpy(scores[i].argmax(axis=1)) - tags = [tag_names[actions[j]] for j in range(len(doc))] - if not self.is_biluo: - tags = iob_to_biluo(tags) - doc.ents = spans_from_biluo_tags(doc, tags) - - def update( - self, - examples: List[Example], - *, - set_annotations: bool = False, - drop: float = 0.0, - sgd: Optional[Optimizer] = None, - losses: Optional[Dict[str, float]] = None, - ) -> Dict[str, float]: - if losses is None: - losses = {} - losses.setdefault("ner", 0.0) - validate_examples(examples, "SimpleNER.update") - if not any(_has_ner(eg) for eg in examples): - return losses - docs = [eg.predicted for eg in examples] - set_dropout_rate(self.model, drop) - scores, bp_scores = self.model.begin_update(docs) - loss, d_scores = self.get_loss(examples, scores) - bp_scores(d_scores) - if set_annotations: - self.set_annotations(docs, scores) - if sgd is not None: - self.model.finish_update(sgd) - losses["ner"] += loss - return losses - - def get_loss(self, examples: List[Example], scores) -> Tuple[List[Floats2d], float]: - validate_examples(examples, "SimpleNER.get_loss") - truths = [] - for eg in examples: - tags = eg.get_aligned_ner() - gold_tags = [(tag if tag != "-" else None) for tag in tags] - if not self.is_biluo: - gold_tags = biluo_to_iob(gold_tags) - truths.append(gold_tags) - for i in range(len(scores)): - if len(scores[i]) != len(truths[i]): - raise ValueError( - f"Mismatched output and gold sizes.\n" - f"Output: {len(scores[i])}, gold: {len(truths[i])}." - f"Input: {len(examples[i].doc)}" - ) - d_scores, loss = self.loss_func(scores, truths) - return loss, d_scores - - def begin_training( - self, - get_examples: Callable[[], Iterable[Example]], - pipeline: Optional[List[Tuple[str, Callable[[Doc], Doc]]]] = None, - sgd: Optional[Optimizer] = None, - ): - self._ensure_examples(get_examples) - all_labels = set() - for example in get_examples(): - all_labels.update(_get_labels(example)) - for label in sorted(all_labels): - if label != "": - self.add_label(label) - doc_sample = [] - label_sample = [] - self._require_labels() - for example in islice(get_examples(), 10): - doc_sample.append(example.x) - gold_tags = example.get_aligned_ner() - if not self.is_biluo: - gold_tags = biluo_to_iob(gold_tags) - gold_array = [ - [1.0 if tag == gold_tag else 0.0 for tag in self.get_tag_names()] - for gold_tag in gold_tags - ] - label_sample.append(self.model.ops.asarray(gold_array, dtype="float32")) - assert len(doc_sample) > 0, Errors.E923.format(name=self.name) - assert len(label_sample) > 0, Errors.E923.format(name=self.name) - self.model.initialize(X=doc_sample, Y=label_sample) - if pipeline is not None: - self.init_multitask_objectives(get_examples, pipeline, sgd=sgd, **self.cfg) - self.loss_func = SequenceCategoricalCrossentropy( - names=self.get_tag_names(), normalize=True, missing_value=None - ) - return sgd - - def init_multitask_objectives(self, *args, **kwargs): - pass - - def score(self, examples, **kwargs): - validate_examples(examples, "SimpleNER.score") - return Scorer.score_spans(examples, "ents", **kwargs) - - -def _has_ner(example: Example) -> bool: - for ner_tag in example.get_aligned_ner(): - if ner_tag != "-" and ner_tag is not None: - return True - else: - return False - - -def _get_labels(example: Example) -> Set[str]: - labels = set() - for ner_tag in example.get_aligned("ENT_TYPE", as_string=True): - if ner_tag != "O" and ner_tag != "-" and ner_tag != "": - labels.add(ner_tag) - return labels diff --git a/spacy/tests/pipeline/test_simple_ner.py b/spacy/tests/pipeline/test_simple_ner.py deleted file mode 100644 index 940743ce0..000000000 --- a/spacy/tests/pipeline/test_simple_ner.py +++ /dev/null @@ -1,106 +0,0 @@ -import pytest -from spacy.lang.en import English -from spacy.training import Example -from spacy import util -from ..util import make_tempdir - - -TRAIN_DATA = [ - ("Who is Shaka S Khan?", {"entities": [(7, 19, "PERSON")]}), - ("I like London and Berlin.", {"entities": [(7, 13, "LOC"), (18, 24, "LOC")]}), -] - - -def test_no_label(): - nlp = English() - nlp.add_pipe("simple_ner") - with pytest.raises(ValueError): - nlp.begin_training() - - -def test_implicit_label(): - nlp = English() - ner = nlp.add_pipe("simple_ner") - train_examples = [] - ner.add_label("ORG") - for t in TRAIN_DATA: - train_examples.append(Example.from_dict(nlp.make_doc(t[0]), t[1])) - nlp.begin_training(get_examples=lambda: train_examples) - - -@pytest.mark.skip(reason="Should be fixed") -def test_untrained(): - # This shouldn't crash, but it does when the simple_ner produces an invalid sequence like ['L-PERSON', 'L-ORG'] - nlp = English() - ner = nlp.add_pipe("simple_ner") - ner.add_label("PERSON") - ner.add_label("LOC") - ner.add_label("ORG") - nlp.begin_training() - nlp("Example sentence") - - -def test_resize(): - nlp = English() - ner = nlp.add_pipe("simple_ner") - ner.add_label("PERSON") - ner.add_label("LOC") - nlp.begin_training() - assert len(ner.labels) == 2 - ner.add_label("ORG") - nlp.begin_training() - assert len(ner.labels) == 3 - - -def test_begin_training_examples(): - nlp = English() - ner = nlp.add_pipe("simple_ner") - train_examples = [] - for text, annotations in TRAIN_DATA: - train_examples.append(Example.from_dict(nlp.make_doc(text), annotations)) - for ent in annotations.get("entities"): - ner.add_label(ent[2]) - # you shouldn't really call this more than once, but for testing it should be fine - nlp.begin_training() - nlp.begin_training(get_examples=lambda: train_examples) - with pytest.raises(TypeError): - nlp.begin_training(get_examples=lambda: None) - with pytest.raises(TypeError): - nlp.begin_training(get_examples=lambda: train_examples[0]) - with pytest.raises(ValueError): - nlp.begin_training(get_examples=lambda: []) - with pytest.raises(ValueError): - nlp.begin_training(get_examples=train_examples) - - -def test_overfitting_IO(): - # Simple test to try and quickly overfit the SimpleNER component - ensuring the ML models work correctly - nlp = English() - ner = nlp.add_pipe("simple_ner") - train_examples = [] - for text, annotations in TRAIN_DATA: - train_examples.append(Example.from_dict(nlp.make_doc(text), annotations)) - optimizer = nlp.begin_training(get_examples=lambda: train_examples) - - for i in range(50): - losses = {} - nlp.update(train_examples, sgd=optimizer, losses=losses) - assert losses["ner"] < 0.0001 - - # test the trained model - test_text = "I like London." - doc = nlp(test_text) - ents = doc.ents - assert len(ents) == 1 - assert ents[0].text == "London" - assert ents[0].label_ == "LOC" - - # Also test the results are still the same after IO - with make_tempdir() as tmp_dir: - nlp.to_disk(tmp_dir) - nlp2 = util.load_model_from_path(tmp_dir) - doc2 = nlp2(test_text) - ents2 = doc2.ents - assert len(ents2) == 1 - assert ents2[0].text == "London" - assert ents2[0].label_ == "LOC" diff --git a/website/docs/api/architectures.md b/website/docs/api/architectures.md index ee844d961..8767c93dd 100644 --- a/website/docs/api/architectures.md +++ b/website/docs/api/architectures.md @@ -456,62 +456,6 @@ consists of either two or three subnetworks: | `nO` | The number of actions the model will predict between. Usually inferred from data at the beginning of training, or loaded from disk. ~~int~~ | | **CREATES** | The model using the architecture. ~~Model[List[Docs], List[List[Floats2d]]]~~ | -### spacy.BILUOTagger.v1 {#BILUOTagger source="spacy/ml/models/simple_ner.py"} - -> #### Example Config -> -> ```ini -> [model] -> @architectures = "spacy.BILUOTagger.v1 " -> -> [model.tok2vec] -> @architectures = "spacy.HashEmbedCNN.v1" -> # etc. -> ``` - -Construct a simple NER tagger that predicts -[BILUO](/usage/linguistic-features#accessing-ner) tag scores for each token and -uses greedy decoding with transition-constraints to return a valid BILUO tag -sequence. A BILUO tag sequence encodes a sequence of non-overlapping labelled -spans into tags assigned to each token. The first token of a span is given the -tag `B-LABEL`, the last token of the span is given the tag `L-LABEL`, and tokens -within the span are given the tag `U-LABEL`. Single-token spans are given the -tag `U-LABEL`. All other tokens are assigned the tag `O`. The BILUO tag scheme -generally results in better linear separation between classes, especially for -non-CRF models, because there are more distinct classes for the different -situations ([Ratinov et al., 2009](https://www.aclweb.org/anthology/W09-1119/)). - -| Name | Description | -| ----------- | ------------------------------------------------------------------------------------------ | -| `tok2vec` | Subnetwork to map tokens into vector representations. ~~Model[List[Doc], List[Floats2d]]~~ | -| **CREATES** | The model using the architecture. ~~Model[List[Doc], List[Floats2d]]~~ | - -### spacy.IOBTagger.v1 {#IOBTagger source="spacy/ml/models/simple_ner.py"} - -> #### Example Config -> -> ```ini -> [model] -> @architectures = "spacy.IOBTagger.v1 " -> -> [model.tok2vec] -> @architectures = "spacy.HashEmbedCNN.v1" -> # etc. -> ``` - -Construct a simple NER tagger, that predicts -[IOB](/usage/linguistic-features#accessing-ner) tag scores for each token and -uses greedy decoding with transition-constraints to return a valid IOB tag -sequence. An IOB tag sequence encodes a sequence of non-overlapping labeled -spans into tags assigned to each token. The first token of a span is given the -tag B-LABEL, and subsequent tokens are given the tag I-LABEL. All other tokens -are assigned the tag O. - -| Name | Description | -| ----------- | ------------------------------------------------------------------------------------------ | -| `tok2vec` | Subnetwork to map tokens into vector representations. ~~Model[List[Doc], List[Floats2d]]~~ | -| **CREATES** | The model using the architecture. ~~Model[List[Doc], List[Floats2d]]~~ | - ## Tagging architectures {#tagger source="spacy/ml/models/tagger.py"} ### spacy.Tagger.v1 {#Tagger} From c89e07927e852711d24028d659d7a09bd8bd2824 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 9 Sep 2020 16:18:38 +0200 Subject: [PATCH 20/51] document individual component API pages --- website/docs/api/dependencyparser.md | 26 ++++++++++++++++++++++++++ website/docs/api/entityrecognizer.md | 26 ++++++++++++++++++++++++++ website/docs/api/morphologizer.md | 8 ++++++++ website/docs/api/tagger.md | 15 +++++++++++---- website/docs/api/textcategorizer.md | 9 ++++++++- 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/website/docs/api/dependencyparser.md b/website/docs/api/dependencyparser.md index 7a09a840a..9fd8f60d2 100644 --- a/website/docs/api/dependencyparser.md +++ b/website/docs/api/dependencyparser.md @@ -307,6 +307,32 @@ Add a new label to the pipe. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | +Note that you don't have to call `pipe.add_label` if you provide a +representative data sample to the [`begin_training`](#begin_training) method. In +this case, all labels found in the sample will be automatically added to the +model, and the output dimension will be +[inferred](/usage/layers-architectures#shape-inference) automatically. + +## DependencyParser.set_output {#set_output tag="method"} + +Change the output dimension of the component's model by calling the model's +attribute `resize_output`. This is a function that takes the original model and +the new output dimension `nO`, and changes the model in place. + +> #### Example +> +> ```python +> parser = nlp.add_pipe("parser") +> parser.set_output(512) +> ``` + +| Name | Description | +| ---- | --------------------------------- | +| `nO` | The new output dimension. ~~int~~ | + +When resizing an already trained model, care should be taken to avoid the +"catastrophic forgetting" problem. + ## DependencyParser.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/entityrecognizer.md b/website/docs/api/entityrecognizer.md index b6b9caa84..51ad984ee 100644 --- a/website/docs/api/entityrecognizer.md +++ b/website/docs/api/entityrecognizer.md @@ -295,6 +295,32 @@ Add a new label to the pipe. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | +Note that you don't have to call `pipe.add_label` if you provide a +representative data sample to the [`begin_training`](#begin_training) method. In +this case, all labels found in the sample will be automatically added to the +model, and the output dimension will be +[inferred](/usage/layers-architectures#shape-inference) automatically. + +## EntityRecognizer.set_output {#set_output tag="method"} + +Change the output dimension of the component's model by calling the model's +attribute `resize_output`. This is a function that takes the original model and +the new output dimension `nO`, and changes the model in place. + +> #### Example +> +> ```python +> ner = nlp.add_pipe("ner") +> ner.set_output(512) +> ``` + +| Name | Description | +| ---- | --------------------------------- | +| `nO` | The new output dimension. ~~int~~ | + +When resizing an already trained model, care should be taken to avoid the +"catastrophic forgetting" problem. + ## EntityRecognizer.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/morphologizer.md b/website/docs/api/morphologizer.md index 069856ea3..120b62b2f 100644 --- a/website/docs/api/morphologizer.md +++ b/website/docs/api/morphologizer.md @@ -258,6 +258,8 @@ context, the original parameters are restored. Add a new label to the pipe. If the `Morphologizer` should set annotations for both `pos` and `morph`, the label should include the UPOS as the feature `POS`. +Raises an error if the output dimension is already set, or if the model has +already been fully [initialized](#begin_training). > #### Example > @@ -271,6 +273,12 @@ both `pos` and `morph`, the label should include the UPOS as the feature `POS`. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | +Note that you don't have to call `pipe.add_label` if you provide a +representative data sample to the [`begin_training`](#begin_training) method. In +this case, all labels found in the sample will be automatically added to the +model, and the output dimension will be +[inferred](/usage/layers-architectures#shape-inference) automatically. + ## Morphologizer.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/tagger.md b/website/docs/api/tagger.md index af0e3af3c..0e929a6ab 100644 --- a/website/docs/api/tagger.md +++ b/website/docs/api/tagger.md @@ -249,9 +249,9 @@ Score a batch of examples. > scores = tagger.score(examples) > ``` -| Name | Description | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `examples` | The examples to score. ~~Iterable[Example]~~ | +| Name | Description | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `examples` | The examples to score. ~~Iterable[Example]~~ | | **RETURNS** | The scores, produced by [`Scorer.score_token_attr`](/api/scorer#score_token_attr) for the attribute `"tag"`. ~~Dict[str, float]~~ | ## Tagger.create_optimizer {#create_optimizer tag="method"} @@ -288,7 +288,8 @@ context, the original parameters are restored. ## Tagger.add_label {#add_label tag="method"} -Add a new label to the pipe. +Add a new label to the pipe. Raises an error if the output dimension is already +set, or if the model has already been fully [initialized](#begin_training). > #### Example > @@ -302,6 +303,12 @@ Add a new label to the pipe. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | +Note that you don't have to call `pipe.add_label` if you provide a +representative data sample to the [`begin_training`](#begin_training) method. In +this case, all labels found in the sample will be automatically added to the +model, and the output dimension will be +[inferred](/usage/layers-architectures#shape-inference) automatically. + ## Tagger.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/textcategorizer.md b/website/docs/api/textcategorizer.md index 927ac5411..e0c7c2f79 100644 --- a/website/docs/api/textcategorizer.md +++ b/website/docs/api/textcategorizer.md @@ -297,7 +297,8 @@ Modify the pipe's model, to use the given parameter values. ## TextCategorizer.add_label {#add_label tag="method"} -Add a new label to the pipe. +Add a new label to the pipe. Raises an error if the output dimension is already +set, or if the model has already been fully [initialized](#begin_training). > #### Example > @@ -311,6 +312,12 @@ Add a new label to the pipe. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | +Note that you don't have to call `pipe.add_label` if you provide a +representative data sample to the [`begin_training`](#begin_training) method. In +this case, all labels found in the sample will be automatically added to the +model, and the output dimension will be +[inferred](/usage/layers-architectures#shape-inference) automatically. + ## TextCategorizer.to_disk {#to_disk tag="method"} Serialize the pipe to disk. From aa27e3f1f22d95717ed5fcd3dc8fd602a393d1ce Mon Sep 17 00:00:00 2001 From: svlandeg Date: Wed, 9 Sep 2020 16:27:21 +0200 Subject: [PATCH 21/51] PyTorch spelling --- website/docs/usage/layers-architectures.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index 95afe3239..894cccc26 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -228,7 +228,7 @@ Thinc allows you to [wrap models](https://thinc.ai/docs/usage-frameworks) written in other machine learning frameworks like PyTorch, TensorFlow and MXNet using a unified [`Model`](https://thinc.ai/docs/api-model) API. -For example, let's use Pytorch to define a very simple Neural network consisting +For example, let's use PyTorch to define a very simple Neural network consisting of two hidden `Linear` layers with `ReLU` activation and dropout, and a softmax-activated output layer. @@ -274,7 +274,7 @@ In the above example, we have combined our custom PyTorch model with a character embedding layer defined by spaCy. [CharacterEmbed](/api/architectures#CharacterEmbed) returns a `Model` that takes a `List[Doc]` as input, and outputs a `List[Floats2d]`. To make sure that the -wrapped Pytorch model receives valid inputs, we use Thinc's +wrapped PyTorch model receives valid inputs, we use Thinc's [`with_array`](https://thinc.ai/docs/api-layers#with_array) helper. As another example, you could have a model where you use PyTorch just for the @@ -284,7 +284,7 @@ consideration for those parts of the network. ## Custom models for trainable components {#custom-models} -To use our custom model including the Pytorch subnetwork, all we need to do is +To use our custom model including the PyTorch subnetwork, all we need to do is register the architecture. The full example then becomes: ```python From 2e567a47c261b3eac22f9cd37abd737d1f48fdfb Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Wed, 9 Sep 2020 21:26:10 +0200 Subject: [PATCH 22/51] Update docs and formatting --- website/docs/api/dependencyparser.md | 19 +-- website/docs/api/entityrecognizer.md | 19 +-- website/docs/api/morphologizer.md | 12 +- website/docs/api/pipe.md | 54 +++--- website/docs/api/tagger.md | 13 +- website/docs/api/textcategorizer.md | 13 +- website/docs/usage/layers-architectures.md | 184 ++++++++++++--------- website/meta/type-annotations.json | 2 + 8 files changed, 165 insertions(+), 151 deletions(-) diff --git a/website/docs/api/dependencyparser.md b/website/docs/api/dependencyparser.md index 9fd8f60d2..ed5e8bdb2 100644 --- a/website/docs/api/dependencyparser.md +++ b/website/docs/api/dependencyparser.md @@ -293,7 +293,11 @@ context, the original parameters are restored. ## DependencyParser.add_label {#add_label tag="method"} -Add a new label to the pipe. +Add a new label to the pipe. Note that you don't have to call this method if you +provide a **representative data sample** to the +[`begin_training`](#begin_training) method. In this case, all labels found in +the sample will be automatically added to the model, and the output dimension +will be [inferred](/usage/layers-architectures#shape-inference) automatically. > #### Example > @@ -307,17 +311,13 @@ Add a new label to the pipe. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | -Note that you don't have to call `pipe.add_label` if you provide a -representative data sample to the [`begin_training`](#begin_training) method. In -this case, all labels found in the sample will be automatically added to the -model, and the output dimension will be -[inferred](/usage/layers-architectures#shape-inference) automatically. - ## DependencyParser.set_output {#set_output tag="method"} Change the output dimension of the component's model by calling the model's attribute `resize_output`. This is a function that takes the original model and -the new output dimension `nO`, and changes the model in place. +the new output dimension `nO`, and changes the model in place. When resizing an +already trained model, care should be taken to avoid the "catastrophic +forgetting" problem. > #### Example > @@ -330,9 +330,6 @@ the new output dimension `nO`, and changes the model in place. | ---- | --------------------------------- | | `nO` | The new output dimension. ~~int~~ | -When resizing an already trained model, care should be taken to avoid the -"catastrophic forgetting" problem. - ## DependencyParser.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/entityrecognizer.md b/website/docs/api/entityrecognizer.md index 51ad984ee..fc6904824 100644 --- a/website/docs/api/entityrecognizer.md +++ b/website/docs/api/entityrecognizer.md @@ -281,7 +281,11 @@ context, the original parameters are restored. ## EntityRecognizer.add_label {#add_label tag="method"} -Add a new label to the pipe. +Add a new label to the pipe. Note that you don't have to call this method if you +provide a **representative data sample** to the +[`begin_training`](#begin_training) method. In this case, all labels found in +the sample will be automatically added to the model, and the output dimension +will be [inferred](/usage/layers-architectures#shape-inference) automatically. > #### Example > @@ -295,17 +299,13 @@ Add a new label to the pipe. | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | -Note that you don't have to call `pipe.add_label` if you provide a -representative data sample to the [`begin_training`](#begin_training) method. In -this case, all labels found in the sample will be automatically added to the -model, and the output dimension will be -[inferred](/usage/layers-architectures#shape-inference) automatically. - ## EntityRecognizer.set_output {#set_output tag="method"} Change the output dimension of the component's model by calling the model's attribute `resize_output`. This is a function that takes the original model and -the new output dimension `nO`, and changes the model in place. +the new output dimension `nO`, and changes the model in place. When resizing an +already trained model, care should be taken to avoid the "catastrophic +forgetting" problem. > #### Example > @@ -318,9 +318,6 @@ the new output dimension `nO`, and changes the model in place. | ---- | --------------------------------- | | `nO` | The new output dimension. ~~int~~ | -When resizing an already trained model, care should be taken to avoid the -"catastrophic forgetting" problem. - ## EntityRecognizer.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/morphologizer.md b/website/docs/api/morphologizer.md index 120b62b2f..c83d3d9fd 100644 --- a/website/docs/api/morphologizer.md +++ b/website/docs/api/morphologizer.md @@ -259,7 +259,11 @@ context, the original parameters are restored. Add a new label to the pipe. If the `Morphologizer` should set annotations for both `pos` and `morph`, the label should include the UPOS as the feature `POS`. Raises an error if the output dimension is already set, or if the model has -already been fully [initialized](#begin_training). +already been fully [initialized](#begin_training). Note that you don't have to +call this method if you provide a **representative data sample** to the +[`begin_training`](#begin_training) method. In this case, all labels found in +the sample will be automatically added to the model, and the output dimension +will be [inferred](/usage/layers-architectures#shape-inference) automatically. > #### Example > @@ -273,12 +277,6 @@ already been fully [initialized](#begin_training). | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | -Note that you don't have to call `pipe.add_label` if you provide a -representative data sample to the [`begin_training`](#begin_training) method. In -this case, all labels found in the sample will be automatically added to the -model, and the output dimension will be -[inferred](/usage/layers-architectures#shape-inference) automatically. - ## Morphologizer.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/pipe.md b/website/docs/api/pipe.md index 7b77141fa..be1279553 100644 --- a/website/docs/api/pipe.md +++ b/website/docs/api/pipe.md @@ -293,12 +293,6 @@ context, the original parameters are restored. > pipe.add_label("MY_LABEL") > ``` - - -This method needs to be overwritten with your own custom `add_label` method. - - - Add a new label to the pipe, to be predicted by the model. The actual implementation depends on the specific component, but in general `add_label` shouldn't be called if the output dimension is already set, or if the model has @@ -308,6 +302,12 @@ the component is [resizable](#is_resizable), in which case [`set_output`](#set_output) should be called to ensure that the model is properly resized. + + +This method needs to be overwritten with your own custom `add_label` method. + + + | Name | Description | | ----------- | ------------------------------------------------------- | | `label` | The label to add. ~~str~~ | @@ -326,41 +326,37 @@ model, and the output dimension will be > ```python > can_resize = pipe.is_resizable() > ``` +> +> ```python +> ### Custom resizing +> def custom_resize(model, new_nO): +> # adjust model +> return model +> +> custom_model.attrs["resize_output"] = custom_resize +> ``` Check whether or not the output dimension of the component's model can be resized. If this method returns `True`, [`set_output`](#set_output) can be called to change the model's output dimension. +For built-in components that are not resizable, you have to create and train a +new model from scratch with the appropriate architecture and output dimension. +For custom components, you can implement a `resize_output` function and add it +as an attribute to the component's model. + | Name | Description | | ----------- | ---------------------------------------------------------------------------------------------- | | **RETURNS** | Whether or not the output dimension of the model can be changed after initialization. ~~bool~~ | -> #### Example -> -> ```python -> def custom_resize(model, new_nO): -> # adjust model -> return model -> custom_model.attrs["resize_output"] = custom_resize -> ``` - -For built-in components that are not resizable, you have to create and train a -new model from scratch with the appropriate architecture and output dimension. - -For custom components, you can implement a `resize_output` function and add it -as an attribute to the component's model. - ## Pipe.set_output {#set_output tag="method"} Change the output dimension of the component's model. If the component is not -[resizable](#is_resizable), this method will throw a `NotImplementedError`. - -If a component is resizable, the model's attribute `resize_output` will be -called. This is a function that takes the original model and the new output -dimension `nO`, and changes the model in place. - -When resizing an already trained model, care should be taken to avoid the -"catastrophic forgetting" problem. +[resizable](#is_resizable), this method will raise a `NotImplementedError`. If a +component is resizable, the model's attribute `resize_output` will be called. +This is a function that takes the original model and the new output dimension +`nO`, and changes the model in place. When resizing an already trained model, +care should be taken to avoid the "catastrophic forgetting" problem. > #### Example > diff --git a/website/docs/api/tagger.md b/website/docs/api/tagger.md index 0e929a6ab..eceb28b19 100644 --- a/website/docs/api/tagger.md +++ b/website/docs/api/tagger.md @@ -289,7 +289,12 @@ context, the original parameters are restored. ## Tagger.add_label {#add_label tag="method"} Add a new label to the pipe. Raises an error if the output dimension is already -set, or if the model has already been fully [initialized](#begin_training). +set, or if the model has already been fully [initialized](#begin_training). Note +that you don't have to call this method if you provide a **representative data +sample** to the [`begin_training`](#begin_training) method. In this case, all +labels found in the sample will be automatically added to the model, and the +output dimension will be [inferred](/usage/layers-architectures#shape-inference) +automatically. > #### Example > @@ -303,12 +308,6 @@ set, or if the model has already been fully [initialized](#begin_training). | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | -Note that you don't have to call `pipe.add_label` if you provide a -representative data sample to the [`begin_training`](#begin_training) method. In -this case, all labels found in the sample will be automatically added to the -model, and the output dimension will be -[inferred](/usage/layers-architectures#shape-inference) automatically. - ## Tagger.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/api/textcategorizer.md b/website/docs/api/textcategorizer.md index e0c7c2f79..0d71655c6 100644 --- a/website/docs/api/textcategorizer.md +++ b/website/docs/api/textcategorizer.md @@ -298,7 +298,12 @@ Modify the pipe's model, to use the given parameter values. ## TextCategorizer.add_label {#add_label tag="method"} Add a new label to the pipe. Raises an error if the output dimension is already -set, or if the model has already been fully [initialized](#begin_training). +set, or if the model has already been fully [initialized](#begin_training). Note +that you don't have to call this method if you provide a **representative data +sample** to the [`begin_training`](#begin_training) method. In this case, all +labels found in the sample will be automatically added to the model, and the +output dimension will be [inferred](/usage/layers-architectures#shape-inference) +automatically. > #### Example > @@ -312,12 +317,6 @@ set, or if the model has already been fully [initialized](#begin_training). | `label` | The label to add. ~~str~~ | | **RETURNS** | `0` if the label is already present, otherwise `1`. ~~int~~ | -Note that you don't have to call `pipe.add_label` if you provide a -representative data sample to the [`begin_training`](#begin_training) method. In -this case, all labels found in the sample will be automatically added to the -model, and the output dimension will be -[inferred](/usage/layers-architectures#shape-inference) automatically. - ## TextCategorizer.to_disk {#to_disk tag="method"} Serialize the pipe to disk. diff --git a/website/docs/usage/layers-architectures.md b/website/docs/usage/layers-architectures.md index 894cccc26..6783f2b7f 100644 --- a/website/docs/usage/layers-architectures.md +++ b/website/docs/usage/layers-architectures.md @@ -5,8 +5,7 @@ menu: - ['Type Signatures', 'type-sigs'] - ['Swapping Architectures', 'swap-architectures'] - ['PyTorch & TensorFlow', 'frameworks'] - - ['Custom Models', 'custom-models'] - - ['Thinc implementation', 'thinc'] + - ['Custom Thinc Models', 'thinc'] - ['Trainable Components', 'components'] next: /usage/projects --- @@ -226,13 +225,24 @@ you'll be able to try it out in any of the spaCy components. ​ Thinc allows you to [wrap models](https://thinc.ai/docs/usage-frameworks) written in other machine learning frameworks like PyTorch, TensorFlow and MXNet -using a unified [`Model`](https://thinc.ai/docs/api-model) API. - -For example, let's use PyTorch to define a very simple Neural network consisting -of two hidden `Linear` layers with `ReLU` activation and dropout, and a -softmax-activated output layer. +using a unified [`Model`](https://thinc.ai/docs/api-model) API. This makes it +easy to use a model implemented in a different framework to power a component in +your spaCy pipeline. For example, to wrap a PyTorch model as a Thinc `Model`, +you can use Thinc's +[`PyTorchWrapper`](https://thinc.ai/docs/api-layers#pytorchwrapper): ```python +from thinc.api import PyTorchWrapper + +wrapped_pt_model = PyTorchWrapper(torch_model) +``` + +Let's use PyTorch to define a very simple neural network consisting of two +hidden `Linear` layers with `ReLU` activation and dropout, and a +softmax-activated output layer: + +```python +### PyTorch model from torch import nn torch_model = nn.Sequential( @@ -246,15 +256,6 @@ torch_model = nn.Sequential( ) ``` -This PyTorch model can be wrapped as a Thinc `Model` by using Thinc's -`PyTorchWrapper`: - -```python -from thinc.api import PyTorchWrapper - -wrapped_pt_model = PyTorchWrapper(torch_model) -``` - The resulting wrapped `Model` can be used as a **custom architecture** as such, or can be a **subcomponent of a larger model**. For instance, we can use Thinc's [`chain`](https://thinc.ai/docs/api-layers#chain) combinator, which works like @@ -273,21 +274,26 @@ model = chain(char_embed, with_array(wrapped_pt_model)) In the above example, we have combined our custom PyTorch model with a character embedding layer defined by spaCy. [CharacterEmbed](/api/architectures#CharacterEmbed) returns a `Model` that takes -a `List[Doc]` as input, and outputs a `List[Floats2d]`. To make sure that the -wrapped PyTorch model receives valid inputs, we use Thinc's +a ~~List[Doc]~~ as input, and outputs a ~~List[Floats2d]~~. To make sure that +the wrapped PyTorch model receives valid inputs, we use Thinc's [`with_array`](https://thinc.ai/docs/api-layers#with_array) helper. -As another example, you could have a model where you use PyTorch just for the -transformer layers, and use "native" Thinc layers to do fiddly input and output -transformations and add on task-specific "heads", as efficiency is less of a -consideration for those parts of the network. +You could also implement a model that only uses PyTorch for the transformer +layers, and "native" Thinc layers to do fiddly input and output transformations +and add on task-specific "heads", as efficiency is less of a consideration for +those parts of the network. -## Custom models for trainable components {#custom-models} +### Using wrapped models {#frameworks-usage} To use our custom model including the PyTorch subnetwork, all we need to do is -register the architecture. The full example then becomes: +register the architecture using the +[`architectures` registry](/api/top-level#registry). This will assign the +architecture a name so spaCy knows how to find it, and allows passing in +arguments like hyperparameters via the [config](/usage/training#config). The +full example then becomes: ```python +### Registering the architecture {highlight="9"} from typing import List from thinc.types import Floats2d from thinc.api import Model, PyTorchWrapper, chain, with_array @@ -297,7 +303,7 @@ from spacy.ml import CharacterEmbed from torch import nn @spacy.registry.architectures("CustomTorchModel.v1") -def TorchModel( +def create_torch_model( nO: int, width: int, hidden_width: int, @@ -321,8 +327,10 @@ def TorchModel( return model ``` -Now you can use this model definition in any existing trainable spaCy component, -by specifying it in the config file: +The model definition can now be used in any existing trainable spaCy component, +by specifying it in the config file. In this configuration, all required +parameters for the various subcomponents of the custom architecture are passed +in as settings via the config. ```ini ### config.cfg (excerpt) {highlight="5-5"} @@ -340,106 +348,124 @@ nC = 8 dropout = 0.2 ``` -In this configuration, we pass all required parameters for the various -subcomponents of the custom architecture as settings in the training config -file. Remember that it is best not to rely on any (hidden) default values, to -ensure that training configs are complete and experiments fully reproducible. + -## Thinc implemention details {#thinc} +Remember that it is best not to rely on any (hidden) default values, to ensure +that training configs are complete and experiments fully reproducible. -Ofcourse it's also possible to define the `Model` from the previous section + + +## Custom models with Thinc {#thinc} + +Of course it's also possible to define the `Model` from the previous section entirely in Thinc. The Thinc documentation provides details on the [various layers](https://thinc.ai/docs/api-layers) and helper functions -available. - -The combinators often used in Thinc can be used to -[overload operators](https://thinc.ai/docs/usage-models#operators). A common -usage is to bind `chain` to `>>`. The "native" Thinc version of our simple -neural network would then become: +available. Combinators can also be used to +[overload operators](https://thinc.ai/docs/usage-models#operators) and a common +usage pattern is to bind `chain` to `>>`. The "native" Thinc version of our +simple neural network would then become: ```python from thinc.api import chain, with_array, Model, Relu, Dropout, Softmax from spacy.ml import CharacterEmbed char_embed = CharacterEmbed(width, embed_size, nM, nC) - with Model.define_operators({">>": chain}): layers = ( - Relu(hidden_width, width) - >> Dropout(dropout) - >> Relu(hidden_width, hidden_width) - >> Dropout(dropout) - >> Softmax(nO, hidden_width) + Relu(hidden_width, width) + >> Dropout(dropout) + >> Relu(hidden_width, hidden_width) + >> Dropout(dropout) + >> Softmax(nO, hidden_width) ) model = char_embed >> with_array(layers) ``` -**⚠️ Note that Thinc layers define the output dimension (`nO`) as the first -argument, followed (optionally) by the input dimension (`nI`). This is in -contrast to how the PyTorch layers are defined, where `in_features` precedes -`out_features`.** + -### Shape inference in thinc {#shape-inference} +Note that Thinc layers define the output dimension (`nO`) as the first argument, +followed (optionally) by the input dimension (`nI`). This is in contrast to how +the PyTorch layers are defined, where `in_features` precedes `out_features`. -It is not strictly necessary to define all the input and output dimensions for -each layer, as Thinc can perform + + +### Shape inference in Thinc {#thinc-shape-inference} + +It is **not** strictly necessary to define all the input and output dimensions +for each layer, as Thinc can perform [shape inference](https://thinc.ai/docs/usage-models#validation) between sequential layers by matching up the output dimensionality of one layer to the input dimensionality of the next. This means that we can simplify the `layers` definition: +> #### Diff +> +> ```diff +> layers = ( +> Relu(hidden_width, width) +> >> Dropout(dropout) +> - >> Relu(hidden_width, hidden_width) +> + >> Relu(hidden_width) +> >> Dropout(dropout) +> - >> Softmax(nO, hidden_width) +> + >> Softmax(nO) +> ) +> ``` + ```python with Model.define_operators({">>": chain}): layers = ( - Relu(hidden_width, width) - >> Dropout(dropout) - >> Relu(hidden_width) - >> Dropout(dropout) - >> Softmax(nO) + Relu(hidden_width, width) + >> Dropout(dropout) + >> Relu(hidden_width) + >> Dropout(dropout) + >> Softmax(nO) ) ``` -Thinc can go one step further and deduce the correct input dimension of the -first layer, and output dimension of the last. To enable this functionality, you -have to call [`model.initialize`](https://thinc.ai/docs/api-model#initialize) -with an input sample `X` and an output sample `Y` with the correct dimensions. +Thinc can even go one step further and **deduce the correct input dimension** of +the first layer, and output dimension of the last. To enable this functionality, +you have to call +[`Model.initialize`](https://thinc.ai/docs/api-model#initialize) with an **input +sample** `X` and an **output sample** `Y` with the correct dimensions: ```python +### Shape inference with initialization {highlight="3,7,10"} with Model.define_operators({">>": chain}): layers = ( - Relu(hidden_width) - >> Dropout(dropout) - >> Relu(hidden_width) - >> Dropout(dropout) - >> Softmax() + Relu(hidden_width) + >> Dropout(dropout) + >> Relu(hidden_width) + >> Dropout(dropout) + >> Softmax() ) model = char_embed >> with_array(layers) model.initialize(X=input_sample, Y=output_sample) ``` The built-in [pipeline components](/usage/processing-pipelines) in spaCy ensure -that their internal models are always initialized with appropriate sample data. -In this case, `X` is typically a `List` of `Doc` objects, while `Y` is a `List` -of 1D or 2D arrays, depending on the specific task. This functionality is -triggered when [`nlp.begin_training`](/api/language#begin_training) is called. +that their internal models are **always initialized** with appropriate sample +data. In this case, `X` is typically a ~~List[Doc]~~, while `Y` is typically a +~~List[Array1d]~~ or ~~List[Array2d]~~, depending on the specific task. This +functionality is triggered when +[`nlp.begin_training`](/api/language#begin_training) is called. -### Dropout and normalization {#drop-norm} +### Dropout and normalization in Thinc {#thinc-dropout-norm} -Many of the `Thinc` layers allow you to define a `dropout` argument that will -result in "chaining" an additional +Many of the available Thinc [layers](https://thinc.ai/docs/api-layers) allow you +to define a `dropout` argument that will result in "chaining" an additional [`Dropout`](https://thinc.ai/docs/api-layers#dropout) layer. Optionally, you can often specify whether or not you want to add layer normalization, which would result in an additional -[`LayerNorm`](https://thinc.ai/docs/api-layers#layernorm) layer. - -That means that the following `layers` definition is equivalent to the previous: +[`LayerNorm`](https://thinc.ai/docs/api-layers#layernorm) layer. That means that +the following `layers` definition is equivalent to the previous: ```python with Model.define_operators({">>": chain}): layers = ( - Relu(hidden_width, dropout=dropout, normalize=False) - >> Relu(hidden_width, dropout=dropout, normalize=False) - >> Softmax() + Relu(hidden_width, dropout=dropout, normalize=False) + >> Relu(hidden_width, dropout=dropout, normalize=False) + >> Softmax() ) model = char_embed >> with_array(layers) model.initialize(X=input_sample, Y=output_sample) diff --git a/website/meta/type-annotations.json b/website/meta/type-annotations.json index b1d94403d..79d4d357d 100644 --- a/website/meta/type-annotations.json +++ b/website/meta/type-annotations.json @@ -34,6 +34,8 @@ "Floats2d": "https://thinc.ai/docs/api-types#types", "Floats3d": "https://thinc.ai/docs/api-types#types", "FloatsXd": "https://thinc.ai/docs/api-types#types", + "Array1d": "https://thinc.ai/docs/api-types#types", + "Array2d": "https://thinc.ai/docs/api-types#types", "Ops": "https://thinc.ai/docs/api-backends#ops", "cymem.Pool": "https://github.com/explosion/cymem", "preshed.BloomFilter": "https://github.com/explosion/preshed", From 0a8455a7fdb9b6978d23f5fe02a3c54649511aff Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 09:33:59 +0200 Subject: [PATCH 23/51] Update lookups data in makefile [ci skip] --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c4e77d101..46a7b22ba 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ SHELL := /bin/bash ifndef SPACY_EXTRAS -override SPACY_EXTRAS = spacy-lookups-data jieba pkuseg==0.0.25 sudachipy sudachidict_core +override SPACY_EXTRAS = spacy-lookups-data==0.4.0.dev0 jieba pkuseg==0.0.25 sudachipy sudachidict_core endif ifndef PYVER From 9073d99fc92efa325c3a994ea54bb8603c78d6ee Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 10:22:59 +0200 Subject: [PATCH 24/51] fix link to shape inference section --- website/docs/api/dependencyparser.md | 3 ++- website/docs/api/entityrecognizer.md | 3 ++- website/docs/api/language.md | 4 ++-- website/docs/api/morphologizer.md | 3 ++- website/docs/api/pipe.md | 2 +- website/docs/api/tagger.md | 4 ++-- website/docs/api/textcategorizer.md | 4 ++-- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/website/docs/api/dependencyparser.md b/website/docs/api/dependencyparser.md index ed5e8bdb2..5bd2ea8ad 100644 --- a/website/docs/api/dependencyparser.md +++ b/website/docs/api/dependencyparser.md @@ -297,7 +297,8 @@ Add a new label to the pipe. Note that you don't have to call this method if you provide a **representative data sample** to the [`begin_training`](#begin_training) method. In this case, all labels found in the sample will be automatically added to the model, and the output dimension -will be [inferred](/usage/layers-architectures#shape-inference) automatically. +will be [inferred](/usage/layers-architectures#thinc-shape-inference) +automatically. > #### Example > diff --git a/website/docs/api/entityrecognizer.md b/website/docs/api/entityrecognizer.md index fc6904824..9189fe763 100644 --- a/website/docs/api/entityrecognizer.md +++ b/website/docs/api/entityrecognizer.md @@ -285,7 +285,8 @@ Add a new label to the pipe. Note that you don't have to call this method if you provide a **representative data sample** to the [`begin_training`](#begin_training) method. In this case, all labels found in the sample will be automatically added to the model, and the output dimension -will be [inferred](/usage/layers-architectures#shape-inference) automatically. +will be [inferred](/usage/layers-architectures#thinc-shape-inference) +automatically. > #### Example > diff --git a/website/docs/api/language.md b/website/docs/api/language.md index 9c9ccb6cf..530f7740d 100644 --- a/website/docs/api/language.md +++ b/website/docs/api/language.md @@ -205,8 +205,8 @@ examples can either be the full training data or a representative sample. They are used to **initialize the models** of trainable pipeline components and are passed each component's [`begin_training`](/api/pipe#begin_training) method, if available. Initialization includes validating the network, -[inferring missing shapes](/usage/layers-architectures#shape-inference) and -setting up the label scheme based on the data. +[inferring missing shapes](/usage/layers-architectures#thinc-shape-inference) +and setting up the label scheme based on the data. If no `get_examples` function is provided when calling `nlp.begin_training`, the pipeline components will be initialized with generic data. In this case, it is diff --git a/website/docs/api/morphologizer.md b/website/docs/api/morphologizer.md index c83d3d9fd..c4787c050 100644 --- a/website/docs/api/morphologizer.md +++ b/website/docs/api/morphologizer.md @@ -263,7 +263,8 @@ already been fully [initialized](#begin_training). Note that you don't have to call this method if you provide a **representative data sample** to the [`begin_training`](#begin_training) method. In this case, all labels found in the sample will be automatically added to the model, and the output dimension -will be [inferred](/usage/layers-architectures#shape-inference) automatically. +will be [inferred](/usage/layers-architectures#thinc-shape-inference) +automatically. > #### Example > diff --git a/website/docs/api/pipe.md b/website/docs/api/pipe.md index be1279553..ec4b0ff1b 100644 --- a/website/docs/api/pipe.md +++ b/website/docs/api/pipe.md @@ -317,7 +317,7 @@ Note that in general, you don't have to call `pipe.add_label` if you provide a representative data sample to the [`begin_training`](#begin_training) method. In this case, all labels found in the sample will be automatically added to the model, and the output dimension will be -[inferred](/usage/layers-architectures#shape-inference) automatically. +[inferred](/usage/layers-architectures#thinc-shape-inference) automatically. ## Pipe.is_resizable {#is_resizable tag="method"} diff --git a/website/docs/api/tagger.md b/website/docs/api/tagger.md index eceb28b19..06def58d5 100644 --- a/website/docs/api/tagger.md +++ b/website/docs/api/tagger.md @@ -293,8 +293,8 @@ set, or if the model has already been fully [initialized](#begin_training). Note that you don't have to call this method if you provide a **representative data sample** to the [`begin_training`](#begin_training) method. In this case, all labels found in the sample will be automatically added to the model, and the -output dimension will be [inferred](/usage/layers-architectures#shape-inference) -automatically. +output dimension will be +[inferred](/usage/layers-architectures#thinc-shape-inference) automatically. > #### Example > diff --git a/website/docs/api/textcategorizer.md b/website/docs/api/textcategorizer.md index 0d71655c6..b296c95ca 100644 --- a/website/docs/api/textcategorizer.md +++ b/website/docs/api/textcategorizer.md @@ -302,8 +302,8 @@ set, or if the model has already been fully [initialized](#begin_training). Note that you don't have to call this method if you provide a **representative data sample** to the [`begin_training`](#begin_training) method. In this case, all labels found in the sample will be automatically added to the model, and the -output dimension will be [inferred](/usage/layers-architectures#shape-inference) -automatically. +output dimension will be +[inferred](/usage/layers-architectures#thinc-shape-inference) automatically. > #### Example > From b7afd09d272e5e9c43bd3bad9d3b448a0c853126 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 11:07:09 +0200 Subject: [PATCH 25/51] Update formatting [ci skip] --- website/docs/api/pipe.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/docs/api/pipe.md b/website/docs/api/pipe.md index be1279553..66b274fcf 100644 --- a/website/docs/api/pipe.md +++ b/website/docs/api/pipe.md @@ -327,8 +327,9 @@ model, and the output dimension will be > can_resize = pipe.is_resizable() > ``` > +> With custom resizing implemented by a component: +> > ```python -> ### Custom resizing > def custom_resize(model, new_nO): > # adjust model > return model From 15bc3a37b4ace8260fbd11c4d2ad0b25080b28de Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 11:08:15 +0200 Subject: [PATCH 26/51] Add --branch to project clone --- spacy/cli/project/clone.py | 5 ++++- website/docs/api/cli.md | 17 +++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/spacy/cli/project/clone.py b/spacy/cli/project/clone.py index a419feb0f..70a640847 100644 --- a/spacy/cli/project/clone.py +++ b/spacy/cli/project/clone.py @@ -16,6 +16,7 @@ def project_clone_cli( name: str = Arg(..., help="The name of the template to clone"), dest: Optional[Path] = Arg(None, help="Where to clone the project. Defaults to current working directory", exists=False), repo: str = Opt(about.__projects__, "--repo", "-r", help="The repository to clone from"), + branch: str = Opt("master", "--branch", "-b", help="The branch to clone from") # fmt: on ): """Clone a project template from a repository. Calls into "git" and will @@ -30,7 +31,9 @@ def project_clone_cli( project_clone(name, dest, repo=repo) -def project_clone(name: str, dest: Path, *, repo: str = about.__projects__) -> None: +def project_clone( + name: str, dest: Path, *, repo: str = about.__projects__, branch: str = "master" +) -> None: """Clone a project template from a repository. name (str): Name of subdirectory to clone. diff --git a/website/docs/api/cli.md b/website/docs/api/cli.md index 0291d6dca..ea61b9ae3 100644 --- a/website/docs/api/cli.md +++ b/website/docs/api/cli.md @@ -893,7 +893,7 @@ can provide any other repo (public or private) that you have access to using the ```cli -$ python -m spacy project clone [name] [dest] [--repo] +$ python -m spacy project clone [name] [dest] [--repo] [--branch] ``` > #### Example @@ -908,13 +908,14 @@ $ python -m spacy project clone [name] [dest] [--repo] > $ python -m spacy project clone template --repo https://github.com/your_org/your_repo > ``` -| Name | Description | -| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | The name of the template to clone, relative to the repo. Can be a top-level directory or a subdirectory like `dir/template`. ~~str (positional)~~ | -| `dest` | Where to clone the project. Defaults to current working directory. ~~Path (positional)~~ | -| `--repo`, `-r` | The repository to clone from. Can be any public or private Git repo you have access to. ~~str (option)~~ | -| `--help`, `-h` | Show help message and available arguments. ~~bool (flag)~~ | -| **CREATES** | The cloned [project directory](/usage/projects#project-files). | +| Name | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | The name of the template to clone, relative to the repo. Can be a top-level directory or a subdirectory like `dir/template`. ~~str (positional)~~ | +| `dest` | Where to clone the project. Defaults to current working directory. ~~Path (positional)~~ | +| `--repo`, `-r` | The repository to clone from. Can be any public or private Git repo you have access to. ~~str (option)~~ | +| `--branch`, `-b` | The branch to clone from. Defaults to `master`. ~~str (option)~~ | +| `--help`, `-h` | Show help message and available arguments. ~~bool (flag)~~ | +| **CREATES** | The cloned [project directory](/usage/projects#project-files). | ### project assets {#project-assets tag="command"} From 1fc54867921c9e5e110e45bd7168fb1cd9337ec1 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 11:31:32 +0200 Subject: [PATCH 27/51] more fine-grained errors for git_sparse_checkout --- spacy/cli/_util.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index ee83ec75e..ea3ff77a8 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -6,6 +6,7 @@ from wasabi import msg import srsly import hashlib import typer +import subprocess from click import NoSuchOption from typer.main import get_command from contextlib import contextmanager @@ -326,22 +327,31 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m f"--filter=blob:none " # <-- The key bit f"-b {branch}" ) - run_command(cmd, capture=True) + _attempt_run_command(cmd) # Now we need to find the missing filenames for the subpath we want. # Looking for this 'rev-list' command in the git --help? Hah. cmd = f"git -C {tmp_dir} rev-list --objects --all --missing=print -- {subpath}" - ret = run_command(cmd, capture=True) + ret = _attempt_run_command(cmd) repo = _from_http_to_git(repo) # Now pass those missings into another bit of git internals missings = " ".join([x[1:] for x in ret.stdout.split() if x.startswith("?")]) + if not missings: + err = f"Could not find any relevant files for '{subpath}'. Did you specify a correct and complete Git path?" + msg.fail(err, exits=1) cmd = f"git -C {tmp_dir} fetch-pack {repo} {missings}" - run_command(cmd, capture=True) + _attempt_run_command(cmd, capture=True) # And finally, we can checkout our subpath cmd = f"git -C {tmp_dir} checkout {branch} {subpath}" - run_command(cmd) + _attempt_run_command(cmd) # We need Path(name) to make sure we also support subdirectories shutil.move(str(tmp_dir / Path(subpath)), str(dest)) +def _attempt_run_command(cmd): + try: + return run_command(cmd, capture=True) + except subprocess.CalledProcessError as e: + err = f"Could not run command: {cmd}." + msg.fail(err, exits=1) def _from_http_to_git(repo): if repo.startswith("http://"): From 92f9d2f406540afa021fe85c77f178c2c9e3cb57 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 11:35:50 +0200 Subject: [PATCH 28/51] small UX fixes --- spacy/cli/_util.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index ea3ff77a8..ae39f7ecb 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -332,14 +332,15 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m # Looking for this 'rev-list' command in the git --help? Hah. cmd = f"git -C {tmp_dir} rev-list --objects --all --missing=print -- {subpath}" ret = _attempt_run_command(cmd) - repo = _from_http_to_git(repo) + git_repo = _from_http_to_git(repo) # Now pass those missings into another bit of git internals missings = " ".join([x[1:] for x in ret.stdout.split() if x.startswith("?")]) if not missings: - err = f"Could not find any relevant files for '{subpath}'. Did you specify a correct and complete Git path?" + err = f"Could not find any relevant files for '{subpath}'. " \ + f"Did you specify a correct and complete path within repo '{repo}'?" msg.fail(err, exits=1) - cmd = f"git -C {tmp_dir} fetch-pack {repo} {missings}" - _attempt_run_command(cmd, capture=True) + cmd = f"git -C {tmp_dir} fetch-pack {git_repo} {missings}" + _attempt_run_command(cmd) # And finally, we can checkout our subpath cmd = f"git -C {tmp_dir} checkout {branch} {subpath}" _attempt_run_command(cmd) From 908f3a449411cd1cedab93ed0e9e7ee0f0f9248d Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 11:42:14 +0200 Subject: [PATCH 29/51] Update default projects repo [ci skip] --- spacy/about.py | 3 ++- spacy/cli/project/clone.py | 8 ++++++-- website/docs/usage/projects.md | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/spacy/about.py b/spacy/about.py index c6176ad36..b8dc65455 100644 --- a/spacy/about.py +++ b/spacy/about.py @@ -4,4 +4,5 @@ __version__ = "3.0.0a16" __release__ = True __download_url__ = "https://github.com/explosion/spacy-models/releases/download" __compatibility__ = "https://raw.githubusercontent.com/explosion/spacy-models/master/compatibility.json" -__projects__ = "https://github.com/explosion/spacy-boilerplates" +__projects__ = "https://github.com/explosion/projects" +__projects_branch__ = "v3" diff --git a/spacy/cli/project/clone.py b/spacy/cli/project/clone.py index 70a640847..c6d261097 100644 --- a/spacy/cli/project/clone.py +++ b/spacy/cli/project/clone.py @@ -16,7 +16,7 @@ def project_clone_cli( name: str = Arg(..., help="The name of the template to clone"), dest: Optional[Path] = Arg(None, help="Where to clone the project. Defaults to current working directory", exists=False), repo: str = Opt(about.__projects__, "--repo", "-r", help="The repository to clone from"), - branch: str = Opt("master", "--branch", "-b", help="The branch to clone from") + branch: str = Opt(about.__projects_branch__, "--branch", "-b", help="The branch to clone from") # fmt: on ): """Clone a project template from a repository. Calls into "git" and will @@ -32,7 +32,11 @@ def project_clone_cli( def project_clone( - name: str, dest: Path, *, repo: str = about.__projects__, branch: str = "master" + name: str, + dest: Path, + *, + repo: str = about.__projects__, + branch: str = about.__projects_branch__, ) -> None: """Clone a project template from a repository. diff --git a/website/docs/usage/projects.md b/website/docs/usage/projects.md index 04edf45bd..2b16b9c18 100644 --- a/website/docs/usage/projects.md +++ b/website/docs/usage/projects.md @@ -65,6 +65,8 @@ project template and copies the files to a local directory. You can then run the project, e.g. to train a pipeline and edit the commands and scripts to build fully custom workflows. + + ```cli python -m spacy project clone some_example_project ``` From a36766d153ebaddae9e2d085621ef32f548c3986 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 12:00:34 +0200 Subject: [PATCH 30/51] hookup branch --- spacy/cli/_util.py | 3 ++- spacy/cli/project/clone.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index ae39f7ecb..b03f3eb69 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -337,7 +337,8 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m missings = " ".join([x[1:] for x in ret.stdout.split() if x.startswith("?")]) if not missings: err = f"Could not find any relevant files for '{subpath}'. " \ - f"Did you specify a correct and complete path within repo '{repo}'?" + f"Did you specify a correct and complete path within repo '{repo}' " \ + f"and branch {branch}?" msg.fail(err, exits=1) cmd = f"git -C {tmp_dir} fetch-pack {git_repo} {missings}" _attempt_run_command(cmd) diff --git a/spacy/cli/project/clone.py b/spacy/cli/project/clone.py index c6d261097..427df490f 100644 --- a/spacy/cli/project/clone.py +++ b/spacy/cli/project/clone.py @@ -28,7 +28,7 @@ def project_clone_cli( """ if dest is None: dest = Path.cwd() / name - project_clone(name, dest, repo=repo) + project_clone(name, dest, repo=repo, branch=branch) def project_clone( @@ -43,13 +43,14 @@ def project_clone( name (str): Name of subdirectory to clone. dest (Path): Destination path of cloned project. repo (str): URL of Git repo containing project templates. + branch (str): The branch to clone from """ dest = ensure_path(dest) check_clone(name, dest, repo) project_dir = dest.resolve() repo_name = re.sub(r"(http(s?)):\/\/github.com/", "", repo) try: - git_sparse_checkout(repo, name, dest) + git_sparse_checkout(repo, name, dest, branch=branch) except subprocess.CalledProcessError: err = f"Could not clone '{name}' from repo '{repo_name}'" msg.fail(err, exits=1) From 763e302dccfe40d11a0030d959541bde1b3853e3 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 13:04:16 +0200 Subject: [PATCH 31/51] Update project widgets and examples [ci skip] --- website/docs/usage/projects.md | 70 +++++++++++++++++----------------- website/src/widgets/project.js | 2 +- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/website/docs/usage/projects.md b/website/docs/usage/projects.md index 2b16b9c18..a1c455cfc 100644 --- a/website/docs/usage/projects.md +++ b/website/docs/usage/projects.md @@ -219,7 +219,7 @@ pipelines. ```yaml -https://github.com/explosion/spacy-boilerplates/blob/master/ner_fashion/project.yml +https://github.com/explosion/projects/tree/v3/tutorials/ner_fashion_brands/project.yml ``` | Section | Description | @@ -815,8 +815,10 @@ package helps you integrate spaCy visualizations into your Streamlit apps and quickly spin up demos to explore your pipelines interactively. It includes a full embedded visualizer, as well as individual components. + + ```bash -$ pip install spacy_streamlit +$ pip install "spacy_streamlit>=1.0.0a0" ``` @@ -828,22 +830,15 @@ $ pip install spacy_streamlit Using [`spacy-streamlit`](https://github.com/explosion/spacy-streamlit), your projects can easily define their own scripts that spin up an interactive visualizer, using the latest pipeline you trained, or a selection of pipelines -so you can compare their results. The following script starts an -[NER visualizer](/usage/visualizers#ent) and takes two positional command-line -argument you can pass in from your `config.yml`: a comma-separated list of paths -to load the pipelines from and an example text to use as the default text. +so you can compare their results. - + -```python -### scripts/visualize.py -import spacy_streamlit -import sys +Get started with spaCy and Streamlit using our project template. It includes a +script to spin up a custom visualizer and commands you can adjust to showcase +and explore your own custom trained pipelines. -DEFAULT_TEXT = sys.argv[2] if len(sys.argv) >= 3 else "" -PIPELINES = [name.strip() for name in sys.argv[1].split(",")] -spacy_streamlit.visualize(PIPELINES, DEFAULT_TEXT, visualizers=["ner"]) -``` + > #### Example usage > @@ -860,16 +855,16 @@ commands: script: - 'streamlit run ./scripts/visualize.py ./training/model-best "I like Adidas shoes."' deps: - - 'training/model-best' + - "training/model-best" ``` - +The following script is called from the `project.yml` and takes two positional +command-line argument: a comma-separated list of paths or packages to load the +pipelines from and an example text to use as the default text. -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus interdum -sodales lectus, ut sodales orci ullamcorper id. Sed condimentum neque ut erat -mattis pretium. - - +```python +https://github.com/explosion/projects/blob/v3/integrations/streamlit/scripts/visualize.py +``` --- @@ -882,9 +877,11 @@ library for serving machine learning models and you can use it in your spaCy projects to quickly serve up a trained pipeline and make it available behind a REST API. -```python -# TODO: show an example that addresses some of the main concerns for serving ML (workers etc.) -``` + + +Get started with spaCy and FastAPI using our project template. + + > #### Example usage > @@ -895,23 +892,24 @@ REST API. ```yaml ### project.yml -commands: - - name: serve - help: "Serve the trained pipeline with FastAPI" + - name: "serve" + help: "Serve the models via a FastAPI REST API using the given host and port" script: - - 'python ./scripts/serve.py ./training/model-best' + - "uvicorn scripts.main:app --reload --host 127.0.0.1 --port 5000" deps: - - 'training/model-best' + - "scripts/main.py" no_skip: true ``` - +The script included in the template shows a simple REST API with a `POST` +endpoint that accepts batches of texts and returns batches of predictions, e.g. +named entities found in the documents. Type hints and +[`pydantic`](https://github.com/samuelcolvin/pydantic) are used to define the +expected data types. -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus interdum -sodales lectus, ut sodales orci ullamcorper id. Sed condimentum neque ut erat -mattis pretium. - - +```python +https://github.com/explosion/projects/blob/v3/integrations/fastapi/scripts/main.py +``` --- diff --git a/website/src/widgets/project.js b/website/src/widgets/project.js index 799de7963..0bd74bc90 100644 --- a/website/src/widgets/project.js +++ b/website/src/widgets/project.js @@ -6,7 +6,7 @@ import Link from '../components/link' import { InlineCode } from '../components/code' // TODO: move to meta? -const DEFAULT_REPO = 'https://github.com/explosion/projects' +const DEFAULT_REPO = 'https://github.com/explosion/projects/tree/v3' const COMMAND = 'python -m spacy project clone' export default function Project({ id, repo, children }) { From 4fec8c39a39fa25317dab071bed670e49c6faf9b Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 13:23:03 +0200 Subject: [PATCH 32/51] Update project teaser [ci skip] --- website/docs/usage/projects.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/usage/projects.md b/website/docs/usage/projects.md index a1c455cfc..137c697b8 100644 --- a/website/docs/usage/projects.md +++ b/website/docs/usage/projects.md @@ -879,7 +879,9 @@ REST API. -Get started with spaCy and FastAPI using our project template. +Get started with spaCy and FastAPI using our project template. It includes a +simple REST API for processing batches of text, and usage examples for how to +query your API from Python and JavaScript (Vanilla JS and React). From 3889747119a6302da96d91502479081ad2036aa4 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 14:36:53 +0200 Subject: [PATCH 33/51] asset fix & UX --- spacy/cli/project/assets.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/spacy/cli/project/assets.py b/spacy/cli/project/assets.py index 2b623675d..0d54a8d05 100644 --- a/spacy/cli/project/assets.py +++ b/spacy/cli/project/assets.py @@ -38,38 +38,45 @@ def project_assets(project_dir: Path) -> None: msg.warn(f"No assets specified in {PROJECT_FILE}", exits=0) msg.info(f"Fetching {len(assets)} asset(s)") for asset in assets: - dest = Path(asset["dest"]) + dest = project_dir / asset["dest"] checksum = asset.get("checksum") - if "git" in asset: - if dest.exists(): - # If there's already a file, check for checksum - if checksum and checksum == get_checksum(dest): - msg.good(f"Skipping download with matching checksum: {dest}") - continue - else: + if dest.exists(): + # If there's already a file, check for checksum + if checksum and checksum == get_checksum(dest): + msg.good(f"Skipping download with matching checksum: {dest}") + continue + else: + msg.good(f"Removing asset with outdated checksum: {dest} ") + if dest.is_dir(): shutil.rmtree(dest) + else: + dest.unlink() + if "git" in asset: git_sparse_checkout( asset["git"]["repo"], asset["git"]["path"], dest, branch=asset["git"].get("branch"), ) - else: + elif "url" in asset: url = asset.get("url") if not url: # project.yml defines asset without URL that the user has to place check_private_asset(dest, checksum) continue fetch_asset(project_path, url, dest, checksum) + else: + msg.warn(f"Could not fetch asset {dest} as neither a 'git' or 'url' parameter is specified.") def check_private_asset(dest: Path, checksum: Optional[str] = None) -> None: """Check and validate assets without a URL (private assets that the user has to provide themselves) and give feedback about the checksum. - dest (Path): Desintation path of the asset. + dest (Path): Destination path of the asset. checksum (Optional[str]): Optional checksum of the expected file. """ + print("path", dest) if not Path(dest).exists(): err = f"No URL provided for asset. You need to add this file yourself: {dest}" msg.warn(err) From f1bc09c1e9ec58b2288d9666339aeebb0c1fa89f Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 14:53:02 +0200 Subject: [PATCH 34/51] restore partly --- spacy/cli/project/assets.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/spacy/cli/project/assets.py b/spacy/cli/project/assets.py index 0d54a8d05..4b59b7751 100644 --- a/spacy/cli/project/assets.py +++ b/spacy/cli/project/assets.py @@ -40,33 +40,30 @@ def project_assets(project_dir: Path) -> None: for asset in assets: dest = project_dir / asset["dest"] checksum = asset.get("checksum") - if dest.exists(): - # If there's already a file, check for checksum - if checksum and checksum == get_checksum(dest): - msg.good(f"Skipping download with matching checksum: {dest}") - continue - else: - msg.good(f"Removing asset with outdated checksum: {dest} ") - if dest.is_dir(): - shutil.rmtree(dest) - else: - dest.unlink() if "git" in asset: + if dest.exists(): + # If there's already a file, check for checksum + if checksum and checksum == get_checksum(dest): + msg.good(f"Skipping download with matching checksum: {dest}") + continue + else: + if dest.is_dir(): + shutil.rmtree(dest) + else: + dest.unlink() git_sparse_checkout( asset["git"]["repo"], asset["git"]["path"], dest, branch=asset["git"].get("branch"), ) - elif "url" in asset: + else: url = asset.get("url") if not url: # project.yml defines asset without URL that the user has to place check_private_asset(dest, checksum) continue fetch_asset(project_path, url, dest, checksum) - else: - msg.warn(f"Could not fetch asset {dest} as neither a 'git' or 'url' parameter is specified.") def check_private_asset(dest: Path, checksum: Optional[str] = None) -> None: @@ -76,7 +73,6 @@ def check_private_asset(dest: Path, checksum: Optional[str] = None) -> None: dest (Path): Destination path of the asset. checksum (Optional[str]): Optional checksum of the expected file. """ - print("path", dest) if not Path(dest).exists(): err = f"No URL provided for asset. You need to add this file yourself: {dest}" msg.warn(err) From 3e83a509bb21d2d315aa1f54f4615d196cb19ea2 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Thu, 10 Sep 2020 15:49:13 +0200 Subject: [PATCH 35/51] WIP: fix project clone compatibility --- spacy/cli/_util.py | 57 ++++++++++++++++++++++++++------------ spacy/cli/project/clone.py | 2 +- spacy/util.py | 2 +- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index b03f3eb69..4694fddbb 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -1,4 +1,4 @@ -from typing import Dict, Any, Union, List, Optional, TYPE_CHECKING +from typing import Dict, Any, Union, List, Optional, Tuple, TYPE_CHECKING import sys import shutil from pathlib import Path @@ -321,41 +321,62 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m # *that* we can do by path. # We're using Git and sparse checkout to only clone the files we need with make_tempdir() as tmp_dir: + git_version = get_git_version() + supports_sparse = git_version >= (2, 22) # This is the "clone, but don't download anything" part. - cmd = ( - f"git clone {repo} {tmp_dir} --no-checkout --depth 1 " - f"--filter=blob:none " # <-- The key bit - f"-b {branch}" - ) + cmd = f"git clone {repo} {tmp_dir} --no-checkout --depth 1 " f"-b {branch} " + if supports_sparse: + cmd += f"--filter=blob:none" # <-- The key bit + else: + msg.warn( + f"You're running an old version of Git (v{git_version[0]}.{git_version[1]}) " + f"that doesn't fully support sparse checkout yet. This means that " + f"more files than necessary may be cloned. To only download the " + f"files needed, upgrade to Git v2.22 or above." + ) _attempt_run_command(cmd) # Now we need to find the missing filenames for the subpath we want. # Looking for this 'rev-list' command in the git --help? Hah. - cmd = f"git -C {tmp_dir} rev-list --objects --all --missing=print -- {subpath}" + cmd = f"git -C {tmp_dir} rev-list --objects --all {'--missing=print ' if supports_sparse else ''} -- {subpath}" ret = _attempt_run_command(cmd) git_repo = _from_http_to_git(repo) # Now pass those missings into another bit of git internals missings = " ".join([x[1:] for x in ret.stdout.split() if x.startswith("?")]) - if not missings: - err = f"Could not find any relevant files for '{subpath}'. " \ - f"Did you specify a correct and complete path within repo '{repo}' " \ - f"and branch {branch}?" - msg.fail(err, exits=1) - cmd = f"git -C {tmp_dir} fetch-pack {git_repo} {missings}" - _attempt_run_command(cmd) + if supports_sparse and not missings: + err = ( + f"Could not find any relevant files for '{subpath}'. " + f"Did you specify a correct and complete path within repo '{repo}' " + f"and branch {branch}?" + ) + msg.fail(err, exits=1) + if supports_sparse: + cmd = f"git -C {tmp_dir} fetch-pack {git_repo} {missings}" + _attempt_run_command(cmd) # And finally, we can checkout our subpath cmd = f"git -C {tmp_dir} checkout {branch} {subpath}" _attempt_run_command(cmd) # We need Path(name) to make sure we also support subdirectories shutil.move(str(tmp_dir / Path(subpath)), str(dest)) -def _attempt_run_command(cmd): + +def get_git_version() -> Tuple[int, int]: + ret = _attempt_run_command(["git", "--version"]) + # TODO: this seems kinda brittle? + version = ret.stdout[11:].strip().split(".") + return (int(version[0]), int(version[1])) + + +def _attempt_run_command(cmd: Union[str, List[str]]): try: return run_command(cmd, capture=True) except subprocess.CalledProcessError as e: - err = f"Could not run command: {cmd}." - msg.fail(err, exits=1) + err = f"Could not run command" + msg.fail(err) + print(cmd) + sys.exit(1) -def _from_http_to_git(repo): + +def _from_http_to_git(repo: str) -> str: if repo.startswith("http://"): repo = repo.replace(r"http://", r"https://") if repo.startswith(r"https://"): diff --git a/spacy/cli/project/clone.py b/spacy/cli/project/clone.py index 427df490f..ab617e4ba 100644 --- a/spacy/cli/project/clone.py +++ b/spacy/cli/project/clone.py @@ -27,7 +27,7 @@ def project_clone_cli( DOCS: https://nightly.spacy.io/api/cli#project-clone """ if dest is None: - dest = Path.cwd() / name + dest = Path.cwd() / Path(name).parts[-1] project_clone(name, dest, repo=repo, branch=branch) diff --git a/spacy/util.py b/spacy/util.py index bd567ddc7..d8df04554 100644 --- a/spacy/util.py +++ b/spacy/util.py @@ -648,7 +648,7 @@ def join_command(command: List[str]) -> str: return " ".join(shlex.quote(cmd) for cmd in command) -def run_command(command: Union[str, List[str]], *, capture=False, stdin=None) -> None: +def run_command(command: Union[str, List[str]], *, capture=False, stdin=None): """Run a command on the command line as a subprocess. If the subprocess returns a non-zero exit code, a system exit is performed. From 08a831ce83c87733369c50c75c82bedb00027ec0 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 17:39:52 +0200 Subject: [PATCH 36/51] process trailing slash if any --- spacy/cli/_util.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index b03f3eb69..c539598ef 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -203,6 +203,10 @@ def get_checksum(path: Union[Path, str]) -> str: msg.fail(f"Can't get checksum for {path}: not a file or directory", exits=1) +def _brol(path): + return str.encode(Path(path).read_text().replace("\r\n", "\n")) + + @contextmanager def show_validation_error( file_path: Optional[Union[str, Path]] = None, @@ -360,5 +364,7 @@ def _from_http_to_git(repo): repo = repo.replace(r"http://", r"https://") if repo.startswith(r"https://"): repo = repo.replace("https://", "git@").replace("/", ":", 1) + if repo.endswith("/"): + repo = repo[:-1] repo = f"{repo}.git" return repo From 1723fb73c42e141b404b8700c49b8a372eec952d Mon Sep 17 00:00:00 2001 From: svlandeg Date: Thu, 10 Sep 2020 17:44:59 +0200 Subject: [PATCH 37/51] remove brol --- spacy/cli/_util.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index c539598ef..0755ccb46 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -203,10 +203,6 @@ def get_checksum(path: Union[Path, str]) -> str: msg.fail(f"Can't get checksum for {path}: not a file or directory", exits=1) -def _brol(path): - return str.encode(Path(path).read_text().replace("\r\n", "\n")) - - @contextmanager def show_validation_error( file_path: Optional[Union[str, Path]] = None, From 6831161bfa776273e0247f6241fea5f05f40623e Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Fri, 11 Sep 2020 09:56:49 +0200 Subject: [PATCH 38/51] Resolve path to be extra sure --- spacy/cli/project/assets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacy/cli/project/assets.py b/spacy/cli/project/assets.py index 4b59b7751..6f23f0e0a 100644 --- a/spacy/cli/project/assets.py +++ b/spacy/cli/project/assets.py @@ -38,7 +38,7 @@ def project_assets(project_dir: Path) -> None: msg.warn(f"No assets specified in {PROJECT_FILE}", exits=0) msg.info(f"Fetching {len(assets)} asset(s)") for asset in assets: - dest = project_dir / asset["dest"] + dest = (project_dir / asset["dest"]).resolve() checksum = asset.get("checksum") if "git" in asset: if dest.exists(): From 761bd60d43748c5b85399d5c1a484f188c6a08de Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Fri, 11 Sep 2020 09:57:00 +0200 Subject: [PATCH 39/51] Adjust info message --- spacy/cli/project/assets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spacy/cli/project/assets.py b/spacy/cli/project/assets.py index 6f23f0e0a..cb3a2fb99 100644 --- a/spacy/cli/project/assets.py +++ b/spacy/cli/project/assets.py @@ -44,7 +44,9 @@ def project_assets(project_dir: Path) -> None: if dest.exists(): # If there's already a file, check for checksum if checksum and checksum == get_checksum(dest): - msg.good(f"Skipping download with matching checksum: {dest}") + msg.good( + f"Skipping download with matching checksum: {asset['dest']}" + ) continue else: if dest.is_dir(): From 1bce432b4af2c3bc3ea98a8bad54690f6ff01ec3 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Fri, 11 Sep 2020 10:00:49 +0200 Subject: [PATCH 40/51] Adjust message [ci skip] --- spacy/cli/_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index 1c4a16f7a..c64aa1507 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -331,8 +331,8 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m msg.warn( f"You're running an old version of Git (v{git_version[0]}.{git_version[1]}) " f"that doesn't fully support sparse checkout yet. This means that " - f"more files than necessary may be cloned. To only download the " - f"files needed, upgrade to Git v2.22 or above." + f"more files than necessary may be downloaded temporarily. To " + f"only download the files needed, upgrade to Git v2.22 or above." ) _attempt_run_command(cmd) # Now we need to find the missing filenames for the subpath we want. From 5b94aeece9b4acc9d303ad8a17b04ce3e11870f2 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Fri, 11 Sep 2020 11:08:46 +0200 Subject: [PATCH 41/51] support pipeline as "list in string" --- spacy/cli/init_config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spacy/cli/init_config.py b/spacy/cli/init_config.py index 584ca7f64..a23b15d53 100644 --- a/spacy/cli/init_config.py +++ b/spacy/cli/init_config.py @@ -42,6 +42,8 @@ def init_config_cli( """ if isinstance(optimize, Optimizations): # instance of enum from the CLI optimize = optimize.value + if pipeline.startswith("[") and pipeline.endswith("]"): + pipeline = pipeline[1:-1] pipeline = [p.strip() for p in pipeline.split(",")] init_config(output_file, lang=lang, pipeline=pipeline, optimize=optimize, cpu=cpu) From 0b2e07215db6eba5b890d480a0642a1cdb013878 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Fri, 11 Sep 2020 11:38:28 +0200 Subject: [PATCH 42/51] Support overwriting name on spacy package --- spacy/cli/package.py | 5 +++++ website/docs/api/cli.md | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/spacy/cli/package.py b/spacy/cli/package.py index c457b3e17..8d6cd84c1 100644 --- a/spacy/cli/package.py +++ b/spacy/cli/package.py @@ -18,6 +18,7 @@ def package_cli( output_dir: Path = Arg(..., help="Output parent directory", exists=True, file_okay=False), meta_path: Optional[Path] = Opt(None, "--meta-path", "--meta", "-m", help="Path to meta.json", exists=True, dir_okay=False), create_meta: bool = Opt(False, "--create-meta", "-c", "-C", help="Create meta.json, even if one exists"), + name: Optional[str] = Opt(None, "--name", "-n", help="Package name to override meta"), version: Optional[str] = Opt(None, "--version", "-v", help="Package version to override meta"), no_sdist: bool = Opt(False, "--no-sdist", "-NS", help="Don't build .tar.gz sdist, can be set if you want to run this step manually"), force: bool = Opt(False, "--force", "-f", "-F", help="Force overwriting existing data in output directory"), @@ -38,6 +39,7 @@ def package_cli( input_dir, output_dir, meta_path=meta_path, + name=name, version=version, create_meta=create_meta, create_sdist=not no_sdist, @@ -50,6 +52,7 @@ def package( input_dir: Path, output_dir: Path, meta_path: Optional[Path] = None, + name: Optional[str] = None, version: Optional[str] = None, create_meta: bool = False, create_sdist: bool = True, @@ -71,6 +74,8 @@ def package( msg.fail("Can't load pipeline meta.json", meta_path, exits=1) meta = srsly.read_json(meta_path) meta = get_meta(input_dir, meta) + if name is not None: + meta["name"] = name if version is not None: meta["version"] = version if not create_meta: # only print if user doesn't want to overwrite diff --git a/website/docs/api/cli.md b/website/docs/api/cli.md index ea61b9ae3..47af9be96 100644 --- a/website/docs/api/cli.md +++ b/website/docs/api/cli.md @@ -852,7 +852,7 @@ this, you can set the `--no-sdist` flag. ```cli -$ python -m spacy package [input_dir] [output_dir] [--meta-path] [--create-meta] [--no-sdist] [--version] [--force] +$ python -m spacy package [input_dir] [output_dir] [--meta-path] [--create-meta] [--no-sdist] [--name] [--version] [--force] ``` > #### Example @@ -870,6 +870,7 @@ $ python -m spacy package [input_dir] [output_dir] [--meta-path] [--create-meta] | `--meta-path`, `-m` 2 | Path to [`meta.json`](/api/data-formats#meta) file (optional). ~~Optional[Path] \(option)~~ | | `--create-meta`, `-C` 2 | Create a `meta.json` file on the command line, even if one already exists in the directory. If an existing file is found, its entries will be shown as the defaults in the command line prompt. ~~bool (flag)~~ | | `--no-sdist`, `-NS`, | Don't build the `.tar.gz` sdist automatically. Can be set if you want to run this step manually. ~~bool (flag)~~ | +| `--name`, `-n` 3 | Package name to override in meta. ~~Optional[str] \(option)~~ | | `--version`, `-v` 3 | Package version to override in meta. Useful when training new versions, as it doesn't require editing the meta template. ~~Optional[str] \(option)~~ | | `--force`, `-f` | Force overwriting of existing folder in output directory. ~~bool (flag)~~ | | `--help`, `-h` | Show help message and available arguments. ~~bool (flag)~~ | From 62eec33bc43150636b9c1a8be561cb9fb4f58425 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Fri, 11 Sep 2020 11:38:33 +0200 Subject: [PATCH 43/51] Fix meta.json validation --- spacy/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spacy/schemas.py b/spacy/schemas.py index baa893802..38f47c668 100644 --- a/spacy/schemas.py +++ b/spacy/schemas.py @@ -180,7 +180,7 @@ class ModelMetaSchema(BaseModel): url: StrictStr = Field("", title="Model author URL") sources: Optional[Union[List[StrictStr], List[Dict[str, str]]]] = Field(None, title="Training data sources") vectors: Dict[str, Any] = Field({}, title="Included word vectors") - labels: Dict[str, Dict[str, List[str]]] = Field({}, title="Component labels, keyed by component name") + labels: Dict[str, List[str]] = Field({}, title="Component labels, keyed by component name") accuracy: Dict[str, Union[float, Dict[str, float]]] = Field({}, title="Accuracy numbers") speed: Dict[str, Union[float, int]] = Field({}, title="Speed evaluation numbers") spacy_git_version: StrictStr = Field("", title="Commit of spaCy version used") From 711166a75a9843fb454d23d795cb77d966ed96e7 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Fri, 11 Sep 2020 15:12:05 +0200 Subject: [PATCH 44/51] prevent overwriting score_weights --- spacy/language.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spacy/language.py b/spacy/language.py index 777b0c24b..70dad59f3 100644 --- a/spacy/language.py +++ b/spacy/language.py @@ -243,7 +243,8 @@ class Language: self._config["nlp"]["pipeline"] = list(self.component_names) self._config["nlp"]["disabled"] = list(self.disabled) self._config["components"] = pipeline - self._config["training"]["score_weights"] = combine_score_weights(score_weights) + if not self._config["training"].get("score_weights"): + self._config["training"]["score_weights"] = combine_score_weights(score_weights) if not srsly.is_json_serializable(self._config): raise ValueError(Errors.E961.format(config=self._config)) return self._config From 115147804a21bf7144f69072e271215d6f51f012 Mon Sep 17 00:00:00 2001 From: svlandeg Date: Sat, 12 Sep 2020 14:43:22 +0200 Subject: [PATCH 45/51] string_to_list to parse comma-separated string into a list --- spacy/cli/_util.py | 32 +++++++++++++++++++++---- spacy/cli/debug_model.py | 5 ++-- spacy/cli/init_config.py | 6 ++--- spacy/tests/test_cli.py | 50 +++++++++++++++++++++++++++++++++++----- 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/spacy/cli/_util.py b/spacy/cli/_util.py index 0755ccb46..1db3a1d44 100644 --- a/spacy/cli/_util.py +++ b/spacy/cli/_util.py @@ -336,10 +336,12 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m # Now pass those missings into another bit of git internals missings = " ".join([x[1:] for x in ret.stdout.split() if x.startswith("?")]) if not missings: - err = f"Could not find any relevant files for '{subpath}'. " \ - f"Did you specify a correct and complete path within repo '{repo}' " \ - f"and branch {branch}?" - msg.fail(err, exits=1) + err = ( + f"Could not find any relevant files for '{subpath}'. " + f"Did you specify a correct and complete path within repo '{repo}' " + f"and branch {branch}?" + ) + msg.fail(err, exits=1) cmd = f"git -C {tmp_dir} fetch-pack {git_repo} {missings}" _attempt_run_command(cmd) # And finally, we can checkout our subpath @@ -348,6 +350,7 @@ def git_sparse_checkout(repo: str, subpath: str, dest: Path, *, branch: str = "m # We need Path(name) to make sure we also support subdirectories shutil.move(str(tmp_dir / Path(subpath)), str(dest)) + def _attempt_run_command(cmd): try: return run_command(cmd, capture=True) @@ -355,6 +358,7 @@ def _attempt_run_command(cmd): err = f"Could not run command: {cmd}." msg.fail(err, exits=1) + def _from_http_to_git(repo): if repo.startswith("http://"): repo = repo.replace(r"http://", r"https://") @@ -364,3 +368,23 @@ def _from_http_to_git(repo): repo = repo[:-1] repo = f"{repo}.git" return repo + + +def string_to_list(value, intify=False): + """Parse a comma-separated string to a list""" + if not value: + return [] + if value.startswith("[") and value.endswith("]"): + value = value[1:-1] + result = [] + for p in value.split(","): + p = p.strip() + if p.startswith("'") and p.endswith("'"): + p = p[1:-1] + if p.startswith('"') and p.endswith('"'): + p = p[1:-1] + p = p.strip() + if intify: + p = int(p) + result.append(p) + return result diff --git a/spacy/cli/debug_model.py b/spacy/cli/debug_model.py index f4d93071e..1a250e43e 100644 --- a/spacy/cli/debug_model.py +++ b/spacy/cli/debug_model.py @@ -5,7 +5,7 @@ from thinc.api import require_gpu, fix_random_seed, set_dropout_rate, Adam from thinc.api import Model, data_validation import typer -from ._util import Arg, Opt, debug_cli, show_validation_error, parse_config_overrides +from ._util import Arg, Opt, debug_cli, show_validation_error, parse_config_overrides, string_to_list from .. import util @@ -38,12 +38,13 @@ def debug_model_cli( require_gpu(use_gpu) else: msg.info("Using CPU") + layers = string_to_list(layers, intify=True) print_settings = { "dimensions": dimensions, "parameters": parameters, "gradients": gradients, "attributes": attributes, - "layers": [int(x.strip()) for x in layers.split(",")] if layers else [], + "layers": layers, "print_before_training": P0, "print_after_init": P1, "print_after_training": P2, diff --git a/spacy/cli/init_config.py b/spacy/cli/init_config.py index a23b15d53..ec65b0e0a 100644 --- a/spacy/cli/init_config.py +++ b/spacy/cli/init_config.py @@ -9,7 +9,7 @@ import re from .. import util from ..language import DEFAULT_CONFIG_PRETRAIN_PATH from ..schemas import RecommendationSchema -from ._util import init_cli, Arg, Opt, show_validation_error, COMMAND +from ._util import init_cli, Arg, Opt, show_validation_error, COMMAND, string_to_list ROOT = Path(__file__).parent / "templates" @@ -42,9 +42,7 @@ def init_config_cli( """ if isinstance(optimize, Optimizations): # instance of enum from the CLI optimize = optimize.value - if pipeline.startswith("[") and pipeline.endswith("]"): - pipeline = pipeline[1:-1] - pipeline = [p.strip() for p in pipeline.split(",")] + pipeline = string_to_list(pipeline) init_config(output_file, lang=lang, pipeline=pipeline, optimize=optimize, cpu=cpu) diff --git a/spacy/tests/test_cli.py b/spacy/tests/test_cli.py index e8c83cbad..0df707dc0 100644 --- a/spacy/tests/test_cli.py +++ b/spacy/tests/test_cli.py @@ -9,6 +9,7 @@ from spacy.cli.pretrain import make_docs from spacy.cli.init_config import init_config, RECOMMENDATIONS from spacy.cli._util import validate_project_commands, parse_config_overrides from spacy.cli._util import load_project_config, substitute_project_variables +from spacy.cli._util import string_to_list from thinc.config import ConfigValidationError import srsly @@ -372,17 +373,13 @@ def test_parse_config_overrides(args, expected): assert parse_config_overrides(args) == expected -@pytest.mark.parametrize( - "args", [["--foo"], ["--x.foo", "bar", "--baz"]], -) +@pytest.mark.parametrize("args", [["--foo"], ["--x.foo", "bar", "--baz"]]) def test_parse_config_overrides_invalid(args): with pytest.raises(NoSuchOption): parse_config_overrides(args) -@pytest.mark.parametrize( - "args", [["--x.foo", "bar", "baz"], ["x.foo"]], -) +@pytest.mark.parametrize("args", [["--x.foo", "bar", "baz"], ["x.foo"]]) def test_parse_config_overrides_invalid_2(args): with pytest.raises(SystemExit): parse_config_overrides(args) @@ -401,3 +398,44 @@ def test_init_config(lang, pipeline, optimize): def test_model_recommendations(): for lang, data in RECOMMENDATIONS.items(): assert RecommendationSchema(**data) + + +@pytest.mark.parametrize( + "value", + [ + # fmt: off + "parser,textcat,tagger", + " parser, textcat ,tagger ", + 'parser,textcat,tagger', + ' parser, textcat ,tagger ', + ' "parser"," textcat " ,"tagger "', + " 'parser',' textcat ' ,'tagger '", + '[parser,textcat,tagger]', + '["parser","textcat","tagger"]', + '[" parser" ,"textcat ", " tagger " ]', + "[parser,textcat,tagger]", + "[ parser, textcat , tagger]", + "['parser','textcat','tagger']", + "[' parser' , 'textcat', ' tagger ' ]", + # fmt: on + ], +) +def test_string_to_list(value): + assert string_to_list(value, intify=False) == ["parser", "textcat", "tagger"] + + +@pytest.mark.parametrize( + "value", + [ + # fmt: off + "1,2,3", + '[1,2,3]', + '["1","2","3"]', + '[" 1" ,"2 ", " 3 " ]', + "[' 1' , '2', ' 3 ' ]", + # fmt: on + ], +) +def test_string_to_list_intify(value): + assert string_to_list(value, intify=False) == ["1", "2", "3"] + assert string_to_list(value, intify=True) == [1, 2, 3] From eedaaaec7534395ad7c19f6016d376a1d1a9a5bf Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Sat, 12 Sep 2020 17:02:53 +0200 Subject: [PATCH 46/51] Fix handling of existing asset without checksum [ci skip] --- spacy/cli/project/assets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spacy/cli/project/assets.py b/spacy/cli/project/assets.py index cb3a2fb99..7326b2e5c 100644 --- a/spacy/cli/project/assets.py +++ b/spacy/cli/project/assets.py @@ -79,7 +79,9 @@ def check_private_asset(dest: Path, checksum: Optional[str] = None) -> None: err = f"No URL provided for asset. You need to add this file yourself: {dest}" msg.warn(err) else: - if checksum and checksum == get_checksum(dest): + if not checksum: + msg.good(f"Asset already exists: {dest}") + elif checksum == get_checksum(dest): msg.good(f"Asset exists with matching checksum: {dest}") else: msg.fail(f"Asset available but with incorrect checksum: {dest}") From 8b0dabe987fa5b6e6bab4fe423deec47498a7d10 Mon Sep 17 00:00:00 2001 From: Ines Montani Date: Sat, 12 Sep 2020 17:05:10 +0200 Subject: [PATCH 47/51] Update docs [ci skip] --- website/README.md | 1 - website/docs/api/attributeruler.md | 2 +- website/docs/api/cli.md | 28 +- website/docs/api/corpus.md | 4 +- website/docs/api/cython.md | 12 +- website/docs/api/data-formats.md | 7 +- website/docs/api/dependencyparser.md | 2 +- website/docs/api/entitylinker.md | 2 +- website/docs/api/entityrecognizer.md | 2 +- website/docs/api/entityruler.md | 2 +- website/docs/api/example.md | 2 +- website/docs/api/language.md | 49 ++-- website/docs/api/lemmatizer.md | 2 +- website/docs/api/morphologizer.md | 2 +- website/docs/api/pipe.md | 2 +- website/docs/api/sentencerecognizer.md | 2 +- website/docs/api/sentencizer.md | 2 +- website/docs/api/tagger.md | 2 +- website/docs/api/textcategorizer.md | 2 +- website/docs/api/tok2vec.md | 2 +- website/docs/api/top-level.md | 55 ++-- website/docs/api/transformer.md | 2 +- website/docs/images/prodigy_overview.jpg | Bin 0 -> 287823 bytes website/docs/images/projects.png | Bin 0 -> 101030 bytes website/docs/images/wandb1.jpg | Bin 0 -> 154793 bytes website/docs/images/wandb2.jpg | Bin 0 -> 149913 bytes website/docs/usage/101/_language-data.md | 39 +-- website/docs/usage/_benchmarks-choi.md | 10 - website/docs/usage/_benchmarks-models.md | 44 +++ website/docs/usage/embeddings-transformers.md | 5 + website/docs/usage/facts-figures.md | 267 +++--------------- website/docs/usage/index.md | 17 +- website/docs/usage/layers-architectures.md | 19 +- website/docs/usage/linguistic-features.md | 34 ++- website/docs/usage/models.md | 2 +- website/docs/usage/projects.md | 70 +++-- website/docs/usage/rule-based-matching.md | 11 +- website/docs/usage/saving-loading.md | 10 +- website/docs/usage/spacy-101.md | 2 +- website/docs/usage/training.md | 4 +- website/docs/usage/v2.md | 4 +- website/docs/usage/v3.md | 162 +++++++++-- website/gatsby-config.js | 15 +- website/meta/logos.json | 37 --- website/meta/site.json | 13 +- website/package-lock.json | 48 ++++ website/package.json | 2 + website/src/components/code.js | 1 + website/src/components/grid.js | 7 +- website/src/components/infobox.js | 2 +- website/src/components/landing.js | 40 +-- website/src/components/table.js | 36 ++- website/src/components/typography.js | 4 +- website/src/components/util.js | 6 +- website/src/images/logos/airbnb.svg | 3 - website/src/images/logos/allenai.svg | 18 -- website/src/images/logos/bbc.svg | 3 - website/src/images/logos/chartbeat.svg | 6 - website/src/images/logos/index.js | 31 -- website/src/images/logos/microsoft.svg | 7 - website/src/images/logos/quora.svg | 3 - website/src/images/logos/recode.svg | 3 - website/src/images/logos/retriever.svg | 3 - website/src/images/logos/stitchfix.svg | 3 - website/src/images/logos/thoughtworks.svg | 3 - website/src/images/logos/uber.svg | 3 - website/src/images/logos/venturebeat.svg | 4 - website/src/images/logos/wapo.svg | 3 - website/src/plugins/remark-code-blocks.js | 5 +- website/src/styles/landing.module.sass | 20 +- website/src/styles/layout.sass | 11 +- website/src/styles/table.module.sass | 18 ++ website/src/templates/index.js | 3 +- website/src/widgets/landing.js | 254 +++++++++++------ website/src/widgets/quickstart-install.js | 9 +- website/src/widgets/quickstart-training.js | 2 +- 76 files changed, 722 insertions(+), 790 deletions(-) create mode 100644 website/docs/images/prodigy_overview.jpg create mode 100644 website/docs/images/projects.png create mode 100644 website/docs/images/wandb1.jpg create mode 100644 website/docs/images/wandb2.jpg delete mode 100644 website/docs/usage/_benchmarks-choi.md create mode 100644 website/docs/usage/_benchmarks-models.md delete mode 100644 website/meta/logos.json delete mode 100644 website/src/images/logos/airbnb.svg delete mode 100644 website/src/images/logos/allenai.svg delete mode 100644 website/src/images/logos/bbc.svg delete mode 100644 website/src/images/logos/chartbeat.svg delete mode 100644 website/src/images/logos/index.js delete mode 100644 website/src/images/logos/microsoft.svg delete mode 100644 website/src/images/logos/quora.svg delete mode 100644 website/src/images/logos/recode.svg delete mode 100644 website/src/images/logos/retriever.svg delete mode 100644 website/src/images/logos/stitchfix.svg delete mode 100644 website/src/images/logos/thoughtworks.svg delete mode 100644 website/src/images/logos/uber.svg delete mode 100644 website/src/images/logos/venturebeat.svg delete mode 100644 website/src/images/logos/wapo.svg diff --git a/website/README.md b/website/README.md index 10a75161b..825d13c65 100644 --- a/website/README.md +++ b/website/README.md @@ -609,7 +609,6 @@ In addition to the native markdown elements, you can use the components ├── docs # the actual markdown content ├── meta # JSON-formatted site metadata | ├── languages.json # supported languages and statistical models -| ├── logos.json # logos and links for landing page | ├── sidebars.json # sidebar navigations for different sections | ├── site.json # general site metadata | └── universe.json # data for the spaCy universe section diff --git a/website/docs/api/attributeruler.md b/website/docs/api/attributeruler.md index fc72eda98..53c8c46cf 100644 --- a/website/docs/api/attributeruler.md +++ b/website/docs/api/attributeruler.md @@ -38,7 +38,7 @@ how the component should be configured. You can override its settings via the | `validate` | Whether patterns should be validated (passed to the `Matcher`). Defaults to `False`. ~~bool~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/attributeruler.py +%%GITHUB_SPACY/spacy/pipeline/attributeruler.py ``` ## AttributeRuler.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/cli.md b/website/docs/api/cli.md index 47af9be96..55e552e72 100644 --- a/website/docs/api/cli.md +++ b/website/docs/api/cli.md @@ -229,13 +229,13 @@ $ python -m spacy convert [input_file] [output_dir] [--converter] [--file-type] ### Converters {#converters} -| ID | Description | -| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `auto` | Automatically pick converter based on file extension and file content (default). | -| `json` | JSON-formatted training data used in spaCy v2.x. | -| `conll` | Universal Dependencies `.conllu` or `.conll` format. | -| `ner` | NER with IOB/IOB2 tags, one token per line with columns separated by whitespace. The first column is the token and the final column is the IOB tag. Sentences are separated by blank lines and documents are separated by the line `-DOCSTART- -X- O O`. Supports CoNLL 2003 NER format. See [sample data](https://github.com/explosion/spaCy/tree/master/examples/training/ner_example_data). | -| `iob` | NER with IOB/IOB2 tags, one sentence per line with tokens separated by whitespace and annotation separated by `|`, either `word|B-ENT` or `word|POS|B-ENT`. See [sample data](https://github.com/explosion/spaCy/tree/master/examples/training/ner_example_data). | +| ID | Description | +| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `auto` | Automatically pick converter based on file extension and file content (default). | +| `json` | JSON-formatted training data used in spaCy v2.x. | +| `conll` | Universal Dependencies `.conllu` or `.conll` format. | +| `ner` | NER with IOB/IOB2 tags, one token per line with columns separated by whitespace. The first column is the token and the final column is the IOB tag. Sentences are separated by blank lines and documents are separated by the line `-DOCSTART- -X- O O`. Supports CoNLL 2003 NER format. See [sample data](%%GITHUB_SPACY/extra/example_data/ner_example_data). | +| `iob` | NER with IOB/IOB2 tags, one sentence per line with tokens separated by whitespace and annotation separated by `|`, either `word|B-ENT` or `word|POS|B-ENT`. See [sample data](%%GITHUB_SPACY/extra/example_data/ner_example_data). | ## debug {#debug new="3"} @@ -358,37 +358,37 @@ File /path/to/spacy/ml/models/tok2vec.py (line 207) Registry @loggers Name spacy.ConsoleLogger.v1 Module spacy.training.loggers -File /path/to/spacy/gold/loggers.py (line 8) +File /path/to/spacy/training/loggers.py (line 8) ℹ [training.batcher] Registry @batchers Name spacy.batch_by_words.v1 Module spacy.training.batchers -File /path/to/spacy/gold/batchers.py (line 49) +File /path/to/spacy/training/batchers.py (line 49) ℹ [training.batcher.size] Registry @schedules Name compounding.v1 Module thinc.schedules -File /Users/ines/Repos/explosion/thinc/thinc/schedules.py (line 43) +File /path/to/thinc/thinc/schedules.py (line 43) ℹ [training.dev_corpus] Registry @readers Name spacy.Corpus.v1 Module spacy.training.corpus -File /path/to/spacy/gold/corpus.py (line 18) +File /path/to/spacy/training/corpus.py (line 18) ℹ [training.optimizer] Registry @optimizers Name Adam.v1 Module thinc.optimizers -File /Users/ines/Repos/explosion/thinc/thinc/optimizers.py (line 58) +File /path/to/thinc/thinc/optimizers.py (line 58) ℹ [training.optimizer.learn_rate] Registry @schedules Name warmup_linear.v1 Module thinc.schedules -File /Users/ines/Repos/explosion/thinc/thinc/schedules.py (line 91) +File /path/to/thinc/thinc/schedules.py (line 91) ℹ [training.train_corpus] Registry @readers Name spacy.Corpus.v1 Module spacy.training.corpus -File /path/to/spacy/gold/corpus.py (line 18) +File /path/to/spacy/training/corpus.py (line 18) ``` diff --git a/website/docs/api/corpus.md b/website/docs/api/corpus.md index b913d9a05..f6f6bbf68 100644 --- a/website/docs/api/corpus.md +++ b/website/docs/api/corpus.md @@ -2,7 +2,7 @@ title: Corpus teaser: An annotated corpus tag: class -source: spacy/gold/corpus.py +source: spacy/training/corpus.py new: 3 --- @@ -42,7 +42,7 @@ streaming. | `limit` | Limit corpus to a subset of examples, e.g. for debugging. Defaults to `0` for no limit. ~~int~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/gold/corpus.py +%%GITHUB_SPACY/spacy/training/corpus.py ``` ## Corpus.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/cython.md b/website/docs/api/cython.md index d7c03cf41..16b11cead 100644 --- a/website/docs/api/cython.md +++ b/website/docs/api/cython.md @@ -23,12 +23,12 @@ abruptly. With Cython there are four ways of declaring complex data types. Unfortunately we use all four in different places, as they all have different utility: -| Declaration | Description | Example | -| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -| `class` | A normal Python class. | [`Language`](/api/language) | -| `cdef class` | A Python extension type. Differs from a normal Python class in that its attributes can be defined on the underlying struct. Can have C-level objects as attributes (notably structs and pointers), and can have methods which have C-level objects as arguments or return types. | [`Lexeme`](/api/cython-classes#lexeme) | -| `cdef struct` | A struct is just a collection of variables, sort of like a named tuple, except the memory is contiguous. Structs can't have methods, only attributes. | [`LexemeC`](/api/cython-structs#lexemec) | -| `cdef cppclass` | A C++ class. Like a struct, this can be allocated on the stack, but can have methods, a constructor and a destructor. Differs from `cdef class` in that it can be created and destroyed without acquiring the Python global interpreter lock. This style is the most obscure. | [`StateC`](https://github.com/explosion/spaCy/tree/master/spacy/syntax/_state.pxd) | +| Declaration | Description | Example | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `class` | A normal Python class. | [`Language`](/api/language) | +| `cdef class` | A Python extension type. Differs from a normal Python class in that its attributes can be defined on the underlying struct. Can have C-level objects as attributes (notably structs and pointers), and can have methods which have C-level objects as arguments or return types. | [`Lexeme`](/api/cython-classes#lexeme) | +| `cdef struct` | A struct is just a collection of variables, sort of like a named tuple, except the memory is contiguous. Structs can't have methods, only attributes. | [`LexemeC`](/api/cython-structs#lexemec) | +| `cdef cppclass` | A C++ class. Like a struct, this can be allocated on the stack, but can have methods, a constructor and a destructor. Differs from `cdef class` in that it can be created and destroyed without acquiring the Python global interpreter lock. This style is the most obscure. | [`StateC`](%%GITHUB_SPACY/spacy/pipeline/_parser_internals/_state.pxd) | The most important classes in spaCy are defined as `cdef class` objects. The underlying data for these objects is usually gathered into a struct, which is diff --git a/website/docs/api/data-formats.md b/website/docs/api/data-formats.md index 6a3b528c6..3d78df39d 100644 --- a/website/docs/api/data-formats.md +++ b/website/docs/api/data-formats.md @@ -37,7 +37,7 @@ recommended settings for your use case, check out the > guide on [registered functions](/usage/training#config-functions) for details. ```ini -https://github.com/explosion/spaCy/blob/develop/spacy/default_config.cfg +%%GITHUB_SPACY/spacy/default_config.cfg ``` @@ -45,8 +45,7 @@ https://github.com/explosion/spaCy/blob/develop/spacy/default_config.cfg Under the hood, spaCy's configs are powered by our machine learning library [Thinc's config system](https://thinc.ai/docs/usage-config), which uses [`pydantic`](https://github.com/samuelcolvin/pydantic/) for data validation -based on type hints. See -[`spacy/schemas.py`](https://github.com/explosion/spaCy/blob/develop/spacy/schemas.py) +based on type hints. See [`spacy/schemas.py`](%%GITHUB_SPACY/spacy/schemas.py) for the schemas used to validate the default config. Arguments of registered functions are validated against their type annotations, if available. To debug your config and check that it's valid, you can run the @@ -456,7 +455,7 @@ lexical data. Here's an example of the 20 most frequent lexemes in the English training data: ```json -https://github.com/explosion/spaCy/tree/master/examples/training/vocab-data.jsonl +%%GITHUB_SPACY / extra / example_data / vocab - data.jsonl ``` ## Pipeline meta {#meta} diff --git a/website/docs/api/dependencyparser.md b/website/docs/api/dependencyparser.md index 5bd2ea8ad..674812567 100644 --- a/website/docs/api/dependencyparser.md +++ b/website/docs/api/dependencyparser.md @@ -57,7 +57,7 @@ architectures and their arguments and hyperparameters. | `model` | The [`Model`](https://thinc.ai/docs/api-model) powering the pipeline component. Defaults to [TransitionBasedParser](/api/architectures#TransitionBasedParser). ~~Model[List[Doc], List[Floats2d]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/dep_parser.pyx +%%GITHUB_SPACY/spacy/pipeline/dep_parser.pyx ``` ## DependencyParser.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/entitylinker.md b/website/docs/api/entitylinker.md index 8cde6c490..a9d45d68e 100644 --- a/website/docs/api/entitylinker.md +++ b/website/docs/api/entitylinker.md @@ -50,7 +50,7 @@ architectures and their arguments and hyperparameters. | `get_candidates` | Function that generates plausible candidates for a given `Span` object. Defaults to [CandidateGenerator](/api/architectures#CandidateGenerator), a function looking up exact, case-dependent aliases in the KB. ~~Callable[[KnowledgeBase, Span], Iterable[Candidate]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/entity_linker.py +%%GITHUB_SPACY/spacy/pipeline/entity_linker.py ``` ## EntityLinker.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/entityrecognizer.md b/website/docs/api/entityrecognizer.md index 9189fe763..1420aa1a7 100644 --- a/website/docs/api/entityrecognizer.md +++ b/website/docs/api/entityrecognizer.md @@ -48,7 +48,7 @@ architectures and their arguments and hyperparameters. | `model` | The [`Model`](https://thinc.ai/docs/api-model) powering the pipeline component. Defaults to [TransitionBasedParser](/api/architectures#TransitionBasedParser). ~~Model[List[Doc], List[Floats2d]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/ner.pyx +%%GITHUB_SPACY/spacy/pipeline/ner.pyx ``` ## EntityRecognizer.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/entityruler.md b/website/docs/api/entityruler.md index 454b2a04b..a6934eeef 100644 --- a/website/docs/api/entityruler.md +++ b/website/docs/api/entityruler.md @@ -42,7 +42,7 @@ how the component should be configured. You can override its settings via the | `ent_id_sep` | Separator used internally for entity IDs. Defaults to `"||"`. ~~str~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/entityruler.py +%%GITHUB_SPACY/spacy/pipeline/entityruler.py ``` ## EntityRuler.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/example.md b/website/docs/api/example.md index 132e9e8f5..668c8028f 100644 --- a/website/docs/api/example.md +++ b/website/docs/api/example.md @@ -2,7 +2,7 @@ title: Example teaser: A training instance tag: class -source: spacy/gold/example.pyx +source: spacy/training/example.pyx new: 3.0 --- diff --git a/website/docs/api/language.md b/website/docs/api/language.md index 530f7740d..c24023177 100644 --- a/website/docs/api/language.md +++ b/website/docs/api/language.md @@ -944,11 +944,11 @@ available to the loaded object. ## Class attributes {#class-attributes} -| Name | Description | -| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `Defaults` | Settings, data and factory methods for creating the `nlp` object and processing pipeline. ~~Defaults~~ | -| `lang` | Two-letter language ID, i.e. [ISO code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). ~~str~~ | -| `default_config` | Base [config](/usage/training#config) to use for [Language.config](/api/language#config). Defaults to [`default_config.cfg`](https://github.com/explosion/spaCy/tree/develop/spacy/default_config.cfg). ~~Config~~ | +| Name | Description | +| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Defaults` | Settings, data and factory methods for creating the `nlp` object and processing pipeline. ~~Defaults~~ | +| `lang` | Two-letter language ID, i.e. [ISO code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). ~~str~~ | +| `default_config` | Base [config](/usage/training#config) to use for [Language.config](/api/language#config). Defaults to [`default_config.cfg`](%%GITHUB_SPACY/spacy/default_config.cfg). ~~Config~~ | ## Defaults {#defaults} @@ -981,34 +981,17 @@ customize the default language data: > config = Config().from_str(DEFAULT_CONFIG) > ``` -| Name | Description | -| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `stop_words` | List of stop words, used for `Token.is_stop`.
**Example:** [`stop_words.py`][stop_words.py] ~~Set[str]~~ | -| `tokenizer_exceptions` | Tokenizer exception rules, string mapped to list of token attributes.
**Example:** [`de/tokenizer_exceptions.py`][de/tokenizer_exceptions.py] ~~Dict[str, List[dict]]~~ | -| `prefixes`, `suffixes`, `infixes` | Prefix, suffix and infix rules for the default tokenizer.
**Example:** [`puncutation.py`][punctuation.py] ~~Optional[List[Union[str, Pattern]]]~~ | -| `token_match` | Optional regex for matching strings that should never be split, overriding the infix rules.
**Example:** [`fr/tokenizer_exceptions.py`][fr/tokenizer_exceptions.py] ~~Optional[Pattern]~~ | -| `url_match` | Regular expression for matching URLs. Prefixes and suffixes are removed before applying the match.
**Example:** [`tokenizer_exceptions.py`][tokenizer_exceptions.py] ~~Optional[Pattern]~~ | -| `lex_attr_getters` | Custom functions for setting lexical attributes on tokens, e.g. `like_num`.
**Example:** [`lex_attrs.py`][lex_attrs.py] ~~Dict[int, Callable[[str], Any]]~~ | -| `syntax_iterators` | Functions that compute views of a `Doc` object based on its syntax. At the moment, only used for [noun chunks](/usage/linguistic-features#noun-chunks).
**Example:** [`syntax_iterators.py`][syntax_iterators.py]. ~~Dict[str, Callable[[Union[Doc, Span]], Iterator[Span]]]~~ | -| `writing_system` | Information about the language's writing system, available via `Vocab.writing_system`. Defaults to: `{"direction": "ltr", "has_case": True, "has_letters": True}.`.
**Example:** [`zh/__init__.py`][zh/__init__.py] ~~Dict[str, Any]~~ | -| `config` | Default [config](/usage/training#config) added to `nlp.config`. This can include references to custom tokenizers or lemmatizers.
**Example:** [`zh/__init__.py`][zh/__init__.py] ~~Config~~ | - -[stop_words.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/en/stop_words.py -[tokenizer_exceptions.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/tokenizer_exceptions.py -[de/tokenizer_exceptions.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/de/tokenizer_exceptions.py -[fr/tokenizer_exceptions.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/fr/tokenizer_exceptions.py -[punctuation.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/punctuation.py -[lex_attrs.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/en/lex_attrs.py -[syntax_iterators.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/en/syntax_iterators.py -[zh/__init__.py]: - https://github.com/explosion/spaCy/tree/master/spacy/lang/zh/__init__.py +| Name | Description | +| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `stop_words` | List of stop words, used for `Token.is_stop`.
**Example:** [`stop_words.py`](%%GITHUB_SPACY/spacy/lang/en/stop_words.py) ~~Set[str]~~ | +| `tokenizer_exceptions` | Tokenizer exception rules, string mapped to list of token attributes.
**Example:** [`de/tokenizer_exceptions.py`](%%GITHUB_SPACY/spacy/lang/de/tokenizer_exceptions.py) ~~Dict[str, List[dict]]~~ | +| `prefixes`, `suffixes`, `infixes` | Prefix, suffix and infix rules for the default tokenizer.
**Example:** [`puncutation.py`](%%GITHUB_SPACY/spacy/lang/punctuation.py) ~~Optional[List[Union[str, Pattern]]]~~ | +| `token_match` | Optional regex for matching strings that should never be split, overriding the infix rules.
**Example:** [`fr/tokenizer_exceptions.py`](%%GITHUB_SPACY/spacy/lang/fr/tokenizer_exceptions.py) ~~Optional[Pattern]~~ | +| `url_match` | Regular expression for matching URLs. Prefixes and suffixes are removed before applying the match.
**Example:** [`tokenizer_exceptions.py`](%%GITHUB_SPACY/spacy/lang/tokenizer_exceptions.py) ~~Optional[Pattern]~~ | +| `lex_attr_getters` | Custom functions for setting lexical attributes on tokens, e.g. `like_num`.
**Example:** [`lex_attrs.py`](%%GITHUB_SPACY/spacy/lang/en/lex_attrs.py) ~~Dict[int, Callable[[str], Any]]~~ | +| `syntax_iterators` | Functions that compute views of a `Doc` object based on its syntax. At the moment, only used for [noun chunks](/usage/linguistic-features#noun-chunks).
**Example:** [`syntax_iterators.py`](%%GITHUB_SPACY/spacy/lang/en/syntax_iterators.py). ~~Dict[str, Callable[[Union[Doc, Span]], Iterator[Span]]]~~ | +| `writing_system` | Information about the language's writing system, available via `Vocab.writing_system`. Defaults to: `{"direction": "ltr", "has_case": True, "has_letters": True}.`.
**Example:** [`zh/__init__.py`](%%GITHUB_SPACY/spacy/lang/zh/__init__.py) ~~Dict[str, Any]~~ | +| `config` | Default [config](/usage/training#config) added to `nlp.config`. This can include references to custom tokenizers or lemmatizers.
**Example:** [`zh/__init__.py`](%%GITHUB_SPACY/spacy/lang/zh/__init__.py) ~~Config~~ | ## Serialization fields {#serialization-fields} diff --git a/website/docs/api/lemmatizer.md b/website/docs/api/lemmatizer.md index 45a8736db..486410907 100644 --- a/website/docs/api/lemmatizer.md +++ b/website/docs/api/lemmatizer.md @@ -56,7 +56,7 @@ data formats used by the lookup and rule-based lemmatizers, see | `model` | **Not yet implemented:** the model to use. ~~Model~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/lemmatizer.py +%%GITHUB_SPACY/spacy/pipeline/lemmatizer.py ``` ## Lemmatizer.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/morphologizer.md b/website/docs/api/morphologizer.md index c4787c050..f2b2f9cc0 100644 --- a/website/docs/api/morphologizer.md +++ b/website/docs/api/morphologizer.md @@ -37,7 +37,7 @@ architectures and their arguments and hyperparameters. | `model` | The model to use. Defaults to [Tagger](/api/architectures#Tagger). ~~Model[List[Doc], List[Floats2d]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/morphologizer.pyx +%%GITHUB_SPACY/spacy/pipeline/morphologizer.pyx ``` ## Morphologizer.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/pipe.md b/website/docs/api/pipe.md index ebbf9ccc4..c8d61a5a9 100644 --- a/website/docs/api/pipe.md +++ b/website/docs/api/pipe.md @@ -22,7 +22,7 @@ for how to use the `Pipe` base class to implement custom components. > inherit from `Pipe`. ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/pipe.pyx +%%GITHUB_SPACY/spacy/pipeline/pipe.pyx ``` ## Pipe.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/sentencerecognizer.md b/website/docs/api/sentencerecognizer.md index 3d9f61e8d..ca19327bb 100644 --- a/website/docs/api/sentencerecognizer.md +++ b/website/docs/api/sentencerecognizer.md @@ -34,7 +34,7 @@ architectures and their arguments and hyperparameters. | `model` | The [`Model`](https://thinc.ai/docs/api-model) powering the pipeline component. Defaults to [Tagger](/api/architectures#Tagger). ~~Model[List[Doc], List[Floats2d]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/senter.pyx +%%GITHUB_SPACY/spacy/pipeline/senter.pyx ``` ## SentenceRecognizer.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/sentencizer.md b/website/docs/api/sentencizer.md index 8104b1151..c435acdcb 100644 --- a/website/docs/api/sentencizer.md +++ b/website/docs/api/sentencizer.md @@ -33,7 +33,7 @@ how the component should be configured. You can override its settings via the | `punct_chars` | Optional custom list of punctuation characters that mark sentence ends. See below for defaults if not set. Defaults to `None`. ~~Optional[List[str]]~~ | `None` | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/sentencizer.pyx +%%GITHUB_SPACY/spacy/pipeline/sentencizer.pyx ``` ## Sentencizer.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/tagger.md b/website/docs/api/tagger.md index 06def58d5..d83a77357 100644 --- a/website/docs/api/tagger.md +++ b/website/docs/api/tagger.md @@ -34,7 +34,7 @@ architectures and their arguments and hyperparameters. | `model` | A model instance that predicts the tag probabilities. The output vectors should match the number of tags in size, and be normalized as probabilities (all scores between 0 and 1, with the rows summing to `1`). Defaults to [Tagger](/api/architectures#Tagger). ~~Model[List[Doc], List[Floats2d]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/tagger.pyx +%%GITHUB_SPACY/spacy/pipeline/tagger.pyx ``` ## Tagger.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/textcategorizer.md b/website/docs/api/textcategorizer.md index b296c95ca..cc20d6fd2 100644 --- a/website/docs/api/textcategorizer.md +++ b/website/docs/api/textcategorizer.md @@ -41,7 +41,7 @@ architectures and their arguments and hyperparameters. | `model` | A model instance that predicts scores for each category. Defaults to [TextCatEnsemble](/api/architectures#TextCatEnsemble). ~~Model[List[Doc], List[Floats2d]]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/textcat.py +%%GITHUB_SPACY/spacy/pipeline/textcat.py ``` ## TextCategorizer.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/tok2vec.md b/website/docs/api/tok2vec.md index deb8369ab..6f13a17a5 100644 --- a/website/docs/api/tok2vec.md +++ b/website/docs/api/tok2vec.md @@ -45,7 +45,7 @@ architectures and their arguments and hyperparameters. | `model` | The model to use. Defaults to [HashEmbedCNN](/api/architectures#HashEmbedCNN). ~~Model[List[Doc], List[Floats2d]~~ | ```python -https://github.com/explosion/spaCy/blob/develop/spacy/pipeline/tok2vec.py +%%GITHUB_SPACY/spacy/pipeline/tok2vec.py ``` ## Tok2Vec.\_\_init\_\_ {#init tag="method"} diff --git a/website/docs/api/top-level.md b/website/docs/api/top-level.md index 7f66abb5f..38e2299fa 100644 --- a/website/docs/api/top-level.md +++ b/website/docs/api/top-level.md @@ -105,8 +105,7 @@ your installation, installed pipelines and local setup from within spaCy. ### spacy.explain {#spacy.explain tag="function"} Get a description for a given POS tag, dependency label or entity type. For a -list of available terms, see -[`glossary.py`](https://github.com/explosion/spaCy/tree/master/spacy/glossary.py). +list of available terms, see [`glossary.py`](%%GITHUB_SPACY/spacy/glossary.py). > #### Example > @@ -262,11 +261,11 @@ If a setting is not present in the options, the default value will be used. > displacy.serve(doc, style="ent", options=options) > ``` -| Name | Description | -| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ents` | Entity types to highlight or `None` for all types (default). ~~Optional[List[str]]~~ | -| `colors` | Color overrides. Entity types should be mapped to color names or values. ~~Dict[str, str]~~ | -| `template` 2.2 | Optional template to overwrite the HTML used to render entity spans. Should be a format string and can use `{bg}`, `{text}` and `{label}`. See [`templates.py`](https://github.com/explosion/spaCy/blob/master/spacy/displacy/templates.py) for examples. ~~Optional[str]~~ | +| Name | Description | +| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ents` | Entity types to highlight or `None` for all types (default). ~~Optional[List[str]]~~ | +| `colors` | Color overrides. Entity types should be mapped to color names or values. ~~Dict[str, str]~~ | +| `template` 2.2 | Optional template to overwrite the HTML used to render entity spans. Should be a format string and can use `{bg}`, `{text}` and `{label}`. See [`templates.py`](GITHUB_SPACY/spacy/displacy/templates.py) for examples. ~~Optional[str]~~ | By default, displaCy comes with colors for all entity types used by [spaCy's trained pipelines](/models). If you're using custom entity types, you @@ -348,7 +347,7 @@ See the [`Transformer`](/api/transformer) API reference and | [`span_getters`](/api/transformer#span_getters) | Registry for functions that take a batch of `Doc` objects and return a list of `Span` objects to process by the transformer, e.g. sentences. | | [`annotation_setters`](/api/transformer#annotation_setters) | Registry for functions that create annotation setters. Annotation setters are functions that take a batch of `Doc` objects and a [`FullTransformerBatch`](/api/transformer#fulltransformerbatch) and can set additional annotations on the `Doc`. | -## Loggers {#loggers source="spacy/gold/loggers.py" new="3"} +## Loggers {#loggers source="spacy/training/loggers.py" new="3"} A logger records the training results. When a logger is created, two functions are returned: one for logging the information for each training step, and a @@ -452,7 +451,7 @@ remain in the config file stored on your local system. | `project_name` | The name of the project in the Weights & Biases interface. The project will be created automatically if it doesn't exist yet. ~~str~~ | | `remove_config_values` | A list of values to include from the config before it is uploaded to W&B (default: empty). ~~List[str]~~ | -## Batchers {#batchers source="spacy/gold/batchers.py" new="3"} +## Batchers {#batchers source="spacy/training/batchers.py" new="3"} A data batcher implements a batching strategy that essentially turns a stream of items into a stream of batches, with each batch consisting of one item or a list @@ -536,7 +535,7 @@ sequences in the batch. | `discard_oversize` | Whether to discard sequences that are by themselves longer than the largest padded batch size. ~~bool~~ | | `get_length` | Optional function that receives a sequence item and returns its length. Defaults to the built-in `len()` if not set. ~~Optional[Callable[[Any], int]]~~ | -## Training data and alignment {#gold source="spacy/gold"} +## Training data and alignment {#gold source="spacy/training"} ### training.biluo_tags_from_offsets {#biluo_tags_from_offsets tag="function"} @@ -616,12 +615,12 @@ token-based tags, e.g. to overwrite the `doc.ents`. ## Utility functions {#util source="spacy/util.py"} spaCy comes with a small collection of utility functions located in -[`spacy/util.py`](https://github.com/explosion/spaCy/tree/master/spacy/util.py). -Because utility functions are mostly intended for **internal use within spaCy**, -their behavior may change with future releases. The functions documented on this -page should be safe to use and we'll try to ensure backwards compatibility. -However, we recommend having additional tests in place if your application -depends on any of spaCy's utilities. +[`spacy/util.py`](%%GITHUB_SPACY/spacy/util.py). Because utility functions are +mostly intended for **internal use within spaCy**, their behavior may change +with future releases. The functions documented on this page should be safe to +use and we'll try to ensure backwards compatibility. However, we recommend +having additional tests in place if your application depends on any of spaCy's +utilities. ### util.get_lang_class {#util.get_lang_class tag="function"} @@ -832,10 +831,10 @@ Compile a sequence of prefix rules into a regex object. > nlp.tokenizer.prefix_search = prefix_regex.search > ``` -| Name | Description | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `entries` | The prefix rules, e.g. [`lang.punctuation.TOKENIZER_PREFIXES`](https://github.com/explosion/spaCy/tree/master/spacy/lang/punctuation.py). ~~Iterable[Union[str, Pattern]]~~ | -| **RETURNS** | The regex object. to be used for [`Tokenizer.prefix_search`](/api/tokenizer#attributes). ~~Pattern~~ | +| Name | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| `entries` | The prefix rules, e.g. [`lang.punctuation.TOKENIZER_PREFIXES`](%%GITHUB_SPACY/spacy/lang/punctuation.py). ~~Iterable[Union[str, Pattern]]~~ | +| **RETURNS** | The regex object. to be used for [`Tokenizer.prefix_search`](/api/tokenizer#attributes). ~~Pattern~~ | ### util.compile_suffix_regex {#util.compile_suffix_regex tag="function"} @@ -849,10 +848,10 @@ Compile a sequence of suffix rules into a regex object. > nlp.tokenizer.suffix_search = suffix_regex.search > ``` -| Name | Description | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `entries` | The suffix rules, e.g. [`lang.punctuation.TOKENIZER_SUFFIXES`](https://github.com/explosion/spaCy/tree/master/spacy/lang/punctuation.py). ~~Iterable[Union[str, Pattern]]~~ | -| **RETURNS** | The regex object. to be used for [`Tokenizer.suffix_search`](/api/tokenizer#attributes). ~~Pattern~~ | +| Name | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| `entries` | The suffix rules, e.g. [`lang.punctuation.TOKENIZER_SUFFIXES`](%%GITHUB_SPACY/spacy/lang/punctuation.py). ~~Iterable[Union[str, Pattern]]~~ | +| **RETURNS** | The regex object. to be used for [`Tokenizer.suffix_search`](/api/tokenizer#attributes). ~~Pattern~~ | ### util.compile_infix_regex {#util.compile_infix_regex tag="function"} @@ -866,10 +865,10 @@ Compile a sequence of infix rules into a regex object. > nlp.tokenizer.infix_finditer = infix_regex.finditer > ``` -| Name | Description | -| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `entries` | The infix rules, e.g. [`lang.punctuation.TOKENIZER_INFIXES`](https://github.com/explosion/spaCy/tree/master/spacy/lang/punctuation.py). ~~Iterable[Union[str, Pattern]]~~ | -| **RETURNS** | The regex object. to be used for [`Tokenizer.infix_finditer`](/api/tokenizer#attributes). ~~Pattern~~ | +| Name | Description | +| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `entries` | The infix rules, e.g. [`lang.punctuation.TOKENIZER_INFIXES`](%%GITHUB_SPACY/spacy/lang/punctuation.py). ~~Iterable[Union[str, Pattern]]~~ | +| **RETURNS** | The regex object. to be used for [`Tokenizer.infix_finditer`](/api/tokenizer#attributes). ~~Pattern~~ | ### util.minibatch {#util.minibatch tag="function" new="2"} diff --git a/website/docs/api/transformer.md b/website/docs/api/transformer.md index fc8a8deef..d5bcef229 100644 --- a/website/docs/api/transformer.md +++ b/website/docs/api/transformer.md @@ -31,7 +31,7 @@ supports all models that are available via the Usually you will connect subsequent components to the shared transformer using the [TransformerListener](/api/architectures#TransformerListener) layer. This works similarly to spaCy's [Tok2Vec](/api/tok2vec) component and -[Tok2VecListener](/api/architectures/Tok2VecListener) sublayer. +[Tok2VecListener](/api/architectures/#Tok2VecListener) sublayer. The component assigns the output of the transformer to the `Doc`'s extension attributes. We also calculate an alignment between the word-piece tokens and the diff --git a/website/docs/images/prodigy_overview.jpg b/website/docs/images/prodigy_overview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..84326cceaaaaa264a383719128ec55e631aa3bd6 GIT binary patch literal 287823 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⁢8wO1u9UHHI|~~d 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
    a+<6q}?k5F^UvbbaY?Am9@JP1ADFn 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