Skip to content

Commit e2a3952

Browse files
authored
Add spacy.TextCatParametricAttention.v1 (#13201)
* Add spacy.TextCatParametricAttention.v1 This layer provides is a simplification of the ensemble classifier that only uses paramteric attention. We have found empirically that with a sufficient amount of training data, using the ensemble classifier with BoW does not provide significant improvement in classifier accuracy. However, plugging in a BoW classifier does reduce GPU training and inference performance substantially, since it uses a GPU-only kernel. * Fix merge fallout
1 parent 7ebba86 commit e2a3952

6 files changed

Lines changed: 115 additions & 4 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ requires = [
55
"cymem>=2.0.2,<2.1.0",
66
"preshed>=3.0.2,<3.1.0",
77
"murmurhash>=0.28.0,<1.1.0",
8-
"thinc>=8.1.8,<8.3.0",
8+
"thinc>=8.2.2,<8.3.0",
99
"numpy>=1.15.0; python_version < '3.9'",
1010
"numpy>=1.25.0; python_version >= '3.9'",
1111
]

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ spacy-legacy>=3.0.11,<3.1.0
33
spacy-loggers>=1.0.0,<2.0.0
44
cymem>=2.0.2,<2.1.0
55
preshed>=3.0.2,<3.1.0
6-
thinc>=8.1.8,<8.3.0
6+
thinc>=8.2.2,<8.3.0
77
ml_datasets>=0.2.0,<0.3.0
88
murmurhash>=0.28.0,<1.1.0
99
wasabi>=0.9.1,<1.2.0

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ setup_requires =
4141
cymem>=2.0.2,<2.1.0
4242
preshed>=3.0.2,<3.1.0
4343
murmurhash>=0.28.0,<1.1.0
44-
thinc>=8.1.8,<8.3.0
44+
thinc>=8.2.2,<8.3.0
4545
install_requires =
4646
# Our libraries
4747
spacy-legacy>=3.0.11,<3.1.0
4848
spacy-loggers>=1.0.0,<2.0.0
4949
murmurhash>=0.28.0,<1.1.0
5050
cymem>=2.0.2,<2.1.0
5151
preshed>=3.0.2,<3.1.0
52-
thinc>=8.1.8,<8.3.0
52+
thinc>=8.2.2,<8.3.0
5353
wasabi>=0.9.1,<1.2.0
5454
srsly>=2.4.3,<3.0.0
5555
catalogue>=2.0.6,<2.1.0

spacy/ml/models/textcat.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
from thinc.api import (
55
Dropout,
6+
Gelu,
67
LayerNorm,
78
Linear,
89
Logistic,
910
Maxout,
1011
Model,
1112
ParametricAttention,
13+
ParametricAttention_v2,
1214
Relu,
1315
Softmax,
1416
SparseLinear,
@@ -146,6 +148,9 @@ def build_text_classifier_v2(
146148
linear_model: Model[List[Doc], Floats2d],
147149
nO: Optional[int] = None,
148150
) -> Model[List[Doc], Floats2d]:
151+
# TODO: build the model with _build_parametric_attention_with_residual_nonlinear
152+
# in spaCy v4. We don't do this in spaCy v3 to preserve model
153+
# compatibility.
149154
exclusive_classes = not linear_model.attrs["multi_label"]
150155
with Model.define_operators({">>": chain, "|": concatenate}):
151156
width = tok2vec.maybe_get_dim("nO")
@@ -211,6 +216,71 @@ def build_text_classifier_lowdata(
211216
return model
212217

213218

219+
@registry.architectures("spacy.TextCatParametricAttention.v1")
220+
def build_textcat_parametric_attention_v1(
221+
tok2vec: Model[List[Doc], List[Floats2d]],
222+
exclusive_classes: bool,
223+
nO: Optional[int] = None,
224+
) -> Model[List[Doc], Floats2d]:
225+
width = tok2vec.maybe_get_dim("nO")
226+
parametric_attention = _build_parametric_attention_with_residual_nonlinear(
227+
tok2vec=tok2vec,
228+
nonlinear_layer=Maxout(nI=width, nO=width),
229+
key_transform=Gelu(nI=width, nO=width),
230+
)
231+
with Model.define_operators({">>": chain}):
232+
if exclusive_classes:
233+
output_layer = Softmax(nO=nO)
234+
else:
235+
output_layer = Linear(nO=nO) >> Logistic()
236+
model = parametric_attention >> output_layer
237+
if model.has_dim("nO") is not False and nO is not None:
238+
model.set_dim("nO", cast(int, nO))
239+
model.set_ref("output_layer", output_layer)
240+
model.attrs["multi_label"] = not exclusive_classes
241+
242+
return model
243+
244+
245+
def _build_parametric_attention_with_residual_nonlinear(
246+
*,
247+
tok2vec: Model[List[Doc], List[Floats2d]],
248+
nonlinear_layer: Model[Floats2d, Floats2d],
249+
key_transform: Optional[Model[Floats2d, Floats2d]] = None,
250+
) -> Model[List[Doc], Floats2d]:
251+
with Model.define_operators({">>": chain, "|": concatenate}):
252+
width = tok2vec.maybe_get_dim("nO")
253+
attention_layer = ParametricAttention_v2(nO=width, key_transform=key_transform)
254+
norm_layer = LayerNorm(nI=width)
255+
parametric_attention = (
256+
tok2vec
257+
>> list2ragged()
258+
>> attention_layer
259+
>> reduce_sum()
260+
>> residual(nonlinear_layer >> norm_layer >> Dropout(0.0))
261+
)
262+
263+
parametric_attention.init = _init_parametric_attention_with_residual_nonlinear
264+
265+
parametric_attention.set_ref("tok2vec", tok2vec)
266+
parametric_attention.set_ref("attention_layer", attention_layer)
267+
parametric_attention.set_ref("nonlinear_layer", nonlinear_layer)
268+
parametric_attention.set_ref("norm_layer", norm_layer)
269+
270+
return parametric_attention
271+
272+
273+
def _init_parametric_attention_with_residual_nonlinear(model, X, Y) -> Model:
274+
tok2vec_width = get_tok2vec_width(model)
275+
model.get_ref("attention_layer").set_dim("nO", tok2vec_width)
276+
model.get_ref("nonlinear_layer").set_dim("nO", tok2vec_width)
277+
model.get_ref("nonlinear_layer").set_dim("nI", tok2vec_width)
278+
model.get_ref("norm_layer").set_dim("nI", tok2vec_width)
279+
model.get_ref("norm_layer").set_dim("nO", tok2vec_width)
280+
init_chain(model, X, Y)
281+
return model
282+
283+
214284
@registry.architectures("spacy.TextCatReduce.v1")
215285
def build_reduce_text_classifier(
216286
tok2vec: Model,

spacy/tests/pipeline/test_textcat.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,9 @@ def test_overfitting_IO_multi():
704704
# CNN V2 (legacy)
705705
("textcat", TRAIN_DATA_SINGLE_LABEL, {"@architectures": "spacy.TextCatCNN.v2", "tok2vec": DEFAULT_TOK2VEC_MODEL, "exclusive_classes": True}),
706706
("textcat_multilabel", TRAIN_DATA_MULTI_LABEL, {"@architectures": "spacy.TextCatCNN.v2", "tok2vec": DEFAULT_TOK2VEC_MODEL, "exclusive_classes": False}),
707+
# PARAMETRIC ATTENTION V1
708+
("textcat", TRAIN_DATA_SINGLE_LABEL, {"@architectures": "spacy.TextCatParametricAttention.v1", "tok2vec": DEFAULT_TOK2VEC_MODEL, "exclusive_classes": True}),
709+
("textcat_multilabel", TRAIN_DATA_MULTI_LABEL, {"@architectures": "spacy.TextCatParametricAttention.v1", "tok2vec": DEFAULT_TOK2VEC_MODEL, "exclusive_classes": False}),
707710
# REDUCE V1
708711
("textcat", TRAIN_DATA_SINGLE_LABEL, {"@architectures": "spacy.TextCatReduce.v1", "tok2vec": DEFAULT_TOK2VEC_MODEL, "exclusive_classes": True, "use_reduce_first": True, "use_reduce_last": True, "use_reduce_max": True, "use_reduce_mean": True}),
709712
("textcat_multilabel", TRAIN_DATA_MULTI_LABEL, {"@architectures": "spacy.TextCatReduce.v1", "tok2vec": DEFAULT_TOK2VEC_MODEL, "exclusive_classes": False, "use_reduce_first": True, "use_reduce_last": True, "use_reduce_max": True, "use_reduce_mean": True}),

website/docs/api/architectures.mdx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,44 @@ the others, but may not be as accurate, especially if texts are short.
10561056
10571057
</Accordion>
10581058
1059+
### spacy.TextCatParametricAttention.v1 {id="TextCatParametricAttention"}
1060+
1061+
> #### Example Config
1062+
>
1063+
> ```ini
1064+
> [model]
1065+
> @architectures = "spacy.TextCatParametricAttention.v1"
1066+
> exclusive_classes = true
1067+
> nO = null
1068+
>
1069+
> [model.tok2vec]
1070+
> @architectures = "spacy.Tok2Vec.v2"
1071+
>
1072+
> [model.tok2vec.embed]
1073+
> @architectures = "spacy.MultiHashEmbed.v2"
1074+
> width = 64
1075+
> rows = [2000, 2000, 1000, 1000, 1000, 1000]
1076+
> attrs = ["ORTH", "LOWER", "PREFIX", "SUFFIX", "SHAPE", "ID"]
1077+
> include_static_vectors = false
1078+
>
1079+
> [model.tok2vec.encode]
1080+
> @architectures = "spacy.MaxoutWindowEncoder.v2"
1081+
> width = ${model.tok2vec.embed.width}
1082+
> window_size = 1
1083+
> maxout_pieces = 3
1084+
> depth = 2
1085+
> ```
1086+
1087+
A neural network model that is built upon Tok2Vec and uses parametric attention
1088+
to attend to tokens that are relevant to text classification.
1089+
1090+
| Name | Description |
1091+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1092+
| `tok2vec` | The `tok2vec` layer to build the neural network upon. ~~Model[List[Doc], List[Floats2d]]~~ |
1093+
| `exclusive_classes` | Whether or not categories are mutually exclusive. ~~bool~~ |
1094+
| `nO` | Output dimension, determined by the number of different labels. If not set, the [`TextCategorizer`](/api/textcategorizer) component will set it when `initialize` is called. ~~Optional[int]~~ |
1095+
| **CREATES** | The model using the architecture. ~~Model[List[Doc], Floats2d]~~ |
1096+
10591097
### spacy.TextCatReduce.v1 {id="TextCatReduce"}
10601098
10611099
> #### Example Config

0 commit comments

Comments
 (0)