Paper · 2020-10-22 · cs.CL
An Analysis of Simple Data Augmentation for Named Entity Recognition
Four simple token-level augmentations for NER (label-wise token replacement, WordNet synonym replacement, mention replacement, shuffle within segments), tested with an LSTM and a SciBERT tagger on two domain datasets. Author-reported: gains are largest on small training sets; applying all four averages +3.3 F1 (LSTM) and +1.8 F1 (SciBERT) across eight columns.
Data augmentationNamed entity recognitionSequence labelingConditional random fieldnamed-entity-recognitiondata-augmentationlow-resourcesequence-labelingbertcrf
arXivPDFCodeMarkdown14 min read
AI-compiled by claude-haiku-5-5 on 2026-10-18, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Named entity recognition finds mentions of things in text and labels each one with its type, and it needs labelled examples. In specialist fields, labelling is slow because only experts can do it, so the labelled sets are small. A common fix is data augmentation: make extra training examples by changing existing ones in ways that keep their meaning.
For sentence-level tasks, such tricks are well known. For entity recognition they are harder, because every word carries a label, so changing a word can break the label. This paper adapts a handful of simple tricks that do not need any other trained model. It swaps a word for another word with the same label, swaps a word for a WordNet synonym, swaps a whole entity for another entity of the same type, or shuffles the words inside a span that shares one label.
The authors test these tricks on two specialist data sets, one on patient reports and one on materials-science text, using two kinds of tagger. Their main finding is that the tricks help most when the training set is small, and that combining them helps more than any one of them alone on average.
TL;DR
- Four token-level augmentations for NER that do not rely on machine translation or parsing models §3.
- Label-wise token replacement and shuffle within segments keep the label sequence unchanged; synonym and mention replacement can change it tab. 1. All four can be combined §3.
- Author-reported: on the small training sets (50 sentences), every method gives a significant gain on both data sets for the LSTM tagger tab. 3.
- The gains shrink on the full training set, and some single methods lower the score there tab. 3 §4.4.
- Results come from one author-reported setup and five random seeds; some gains are smaller than one standard deviation tab. 3.
Problem
Modern neural taggers need many labelled sentences. In biomedical and materials-science text, expert annotation is slow and costly, so the labelled sets are small §1. Data augmentation adds transformed copies of training examples without changing their labels §1. Text-classification and translation methods use augmentation, but NER predicts a label for every token, so a change to one token can change that token's label §1. The paper asks which simple augmentations still work for NER and under which training-set sizes §1 §4.4.
Method
The paper surveys word replacement, mention replacement, word swapping and generative augmentation for sentence-level tasks §2. It then adapts four methods to token-level labels, with no external models §3. Each method takes a sentence with BIO labels and produces a new sentence with BIO labels. A replacement is decided per unit (token, mention or segment) with a binomial draw with probability p §3. The paper's Table 1 shows one sentence transformed by each method tab. 1.
Labels. Each token carries a BIO tag combined with an entity type §4.2. Table 1 shows one example sentence and one augmented variant per method, with changed tokens highlighted and the label row shown for each variant tab. 1.
Label-wise token replacement (LwTR) §3. For each token, draw a binomial variable with probability p. If it is 1, replace the token with another token drawn from a distribution over tokens that share the same label, built from the original training set. The label sequence stays unchanged.
Synonym replacement (SR) §3. The same token-level draw as LwTR, but the replacement is a WordNet synonym of the token. A synonym can have several words. The first word keeps the token's label. If the token starts a mention (B), the other words get I of the same type. If the token is inside a mention (I), all words get that label.
Mention replacement (MR) §3. For each mention, draw a binomial variable with probability p. If it is 1, replace the whole mention with another mention of the same entity type from the training set. The BIO labels are rewritten to match the new mention's length.
Shuffle within segments (SiS) §3. Split the sentence into segments. Each mention is one segment and each run of out-of-mention tokens is one segment. Each segment is shuffled with probability p. Only the token order inside a segment changes; the label order stays the same.
All §3. Create one augmented instance per method for each training instance. The paper does not describe any other way of combining the methods.
# Label-wise token replacement (LwTR) [§3] [code: augment.py#L156-L171]
def label_wise_token_replace(sentence, token_dist, p):
# Keep stopwords and unselected tokens as they are [code: augment.py#L163]
augmented = []
for token, label in sentence:
if is_stopword(token) or not bernoulli(p):
augmented.append((token, label))
else:
# Draw a token that already carries this full BIO label [§3]
new_token = sample(token_dist[label])
augmented.append((new_token, label))
return augmented# Synonym replacement (SR) [§3] [code: augment.py#L174-L206]
def synonym_replace(sentence, p):
augmented = []
for token, label in sentence:
# Stopwords and unselected tokens are kept [code: augment.py#L181]
if is_stopword(token) or not bernoulli(p):
augmented.append((token, label))
continue
# Pool WordNet lemma names of all senses [code: augment.py#L186-L192]
synonyms = wordnet_synonyms(token) - {token}
if len(synonyms) == 0:
augmented.append((token, label))
continue
# Pick one synonym at random [§3] [code: augment.py#L199]
words = random_choice(list(synonyms)).split()
for i, word in enumerate(words):
# The first word keeps the label; B-x gives I-x to the rest [§3]
word_label = label
if i > 0 and label.startswith("B-"):
word_label = "I-" + label[2:]
augmented.append((word, word_label))
return augmented# Mention replacement (MR) [§3] [code: augment.py#L43-L83]
def mention_replace(sentence, mentions_by_type, p):
augmented = []
position = 0
while position < len(sentence):
token, label = sentence[position]
if label == "O":
# Tokens outside mentions are copied [code: augment.py#L49-L52]
augmented.append((token, label))
position += 1
continue
# Find the end of this mention [code: augment.py#L70-L76]
end = mention_end(sentence, position)
if bernoulli(p):
# Swap in a random training mention of the same type [§3]
new_words = random_choice(mentions_by_type[label[2:]])
augmented += labelled_words(new_words, label[2:])
else:
augmented += sentence[position:end]
position = end
return augmented# Shuffle within segments (SiS) [§3] [code: augment.py#L86-L131]
def shuffle_within_segments(sentence, p):
# One segment per mention and per run of O tokens [§3]
segments = split_into_segments(sentence)
augmented = []
for segment in segments:
# Shuffle a segment of more than one token with probability p [§3]
if len(segment) > 1 and bernoulli(p):
tokens = random_permutation(segment.tokens)
else:
tokens = segment.tokens
# Labels stay in position; only the tokens move [§3]
augmented += zip(tokens, segment.labels)
return augmentedHyperparameters
The paper gives the values below in its text and tables. The "code default" column lists what the released code uses when no flag is passed; it is not a paper value. "not stated" means the paper does not give the value.
| Name | Value | Where | Notes |
|---|---|---|---|
| Encoder A (LSTM) | BiLSTM over GloVe token embeddings, trained from scratch | §4.2 | LSTM size, layers and GloVe version not stated. The released code has no LSTM encoder models.py |
| Encoder B (BERT) | SciBERT, pretrained on scientific articles, fine-tuned for NER; first sub-token of each word is the representation | §4.2 | Cased vocabulary named only in the README README.md |
| Output layer | Conditional random field over BIO tags | §4.2 | Transition constraints not described in the paper |
| Metric | Micro-averaged span-level F1, string match | §4.2 | Code also requires identical token positions train.py |
| Model selection | Checkpoint with the best dev F1 is evaluated on test | §4.2 | Code matches train.py |
| Replace probability p | Searched from 0.1 to 0.7, step 0.2 (values 0.1, 0.3, 0.5, 0.7 inferred from the range) | §4.3 | Code default 0.3 main.py |
| Augmented instances per sentence | {1, 3, 6, 10} for one method; {1, 2, 3} for All | §4.3 | Code default 1 main.py |
| Search | Grid search on dev F1 over p and instance count | §4.3 | Search code not in repository |
| Training subsets | First 50, 150, 500 sentences containing at least one mention, and the full set | §4.1 | Subset creation code not in repository |
| Dev split | i2b2-2010: 15% of training sentences; MaSciP: authors' split | §4.1 | |
| Repetitions | Five random seeds per cell | tab. 3 caption | Seed values not stated |
| Optimiser | not stated | §4.3 | Code: AdamW for the default "adam" train.py |
| Learning rate | not stated | §4.3 | Code default 3e-5, one rate for all parameters main.py train.py |
| Batch size | not stated | §4.3 | Code default 16 for train and eval main.py |
| Epochs and early stopping | not stated | §4.3 | Code: at most 100 epochs, stop after 10 epochs without dev gain main.py main.py |
| LR schedule | not stated | §4.3 | Code: ReduceLROnPlateau on dev F1, factor 0.5, patience 3, stop below 1e-8 main.py main.py |
| Gradient clipping | not stated | §4.3 | Code: norm 5.0, applied to encoder, MLP and CRF separately train.py |
| Dropout | not stated | §4.3 | Code: 0.4 on encoder output and word dropout 0.05 main.py |
| Seed | not stated | §4.3 | Code default 52 main.py |
| Smoothing exponent | not stated | — | Code default 1.0, which leaves the distribution unchanged main.py |
Data set sizes tab. 2:
| Data set | Split | Sentences | Tokens | Mentions | Entity types |
|---|---|---|---|---|---|
| MaSciP | Train | 1,901 | 61,750 | 18,874 | 21 |
| MaSciP | Dev | 109 | 4,158 | 1,190 | 20 |
| MaSciP | Test | 158 | 4,585 | 1,259 | 21 |
| i2b2-2010 | Train | 13,868 | 129,087 | 14,376 | 3 |
| i2b2-2010 | Dev | 2,447 | 20,454 | 2,143 | 3 |
| i2b2-2010 | Test | 27,625 | 267,249 | 31,161 | 3 |
Results
Author-reported. Setup: MaSciP and i2b2-2010 §4.1; an LSTM tagger with GloVe and a SciBERT tagger, both with a CRF §4.2; micro-averaged span F1 on the test set, mean and standard deviation over five seeds tab. 3. The Δ column is the mean of the eight differences from the no-augmentation row, which the table bears out for the first two rows. "*" marks a cell the paper underlines, which the caption defines as significantly better than the baseline under a paired t-test at p = 0.05 tab. 3. Columns are S, M, L (50, 150, 500 training sentences) and F (full set) tab. 3.
| Model | Method | MaSciP S | MaSciP M | MaSciP L | MaSciP F | i2b2 S | i2b2 M | i2b2 L | i2b2 F | Δ |
|---|---|---|---|---|---|---|---|---|---|---|
| LSTM | No augmentation | 53.0 ± 3.2 | 63.0 ± 0.6 | 70.3 ± 0.8 | 76.4 ± 0.4 | 17.1 ± 2.0 | 43.3 ± 1.2 | 54.1 ± 0.6 | 81.1 ± 0.2 | |
| LSTM | Label-wise token rep. | 59.7 ± 0.6* | 65.5 ± 0.6* | 71.4 ± 0.4* | 76.3 ± 0.8 | 26.7 ± 0.8* | 44.3 ± 0.8* | 54.5 ± 0.8 | 81.0 ± 0.2 | 2.6 |
| LSTM | Synonym replacement | 60.1 ± 0.5* | 65.4 ± 0.4* | 70.8 ± 0.6 | 76.7 ± 0.8 | 25.9 ± 0.5* | 44.1 ± 0.5 | 54.4 ± 1.5 | 81.0 ± 0.3 | 2.5 |
| LSTM | Mention replacement | 60.6 ± 0.6* | 65.4 ± 0.4* | 71.9 ± 0.5* | 76.0 ± 0.8 | 25.9 ± 0.7* | 45.5 ± 0.4* | 55.0 ± 0.2* | 81.4 ± 0.2* | 2.9 |
| LSTM | Shuffle within segments | 58.8 ± 0.7* | 64.6 ± 0.4* | 70.5 ± 0.8 | 77.1 ± 0.3* | 25.2 ± 0.5* | 44.4 ± 0.6 | 53.5 ± 0.9 | 80.6 ± 0.3 | 2.0 |
| LSTM | All | 60.8 ± 1.3* | 67.0 ± 0.8* | 72.1 ± 0.7* | 76.6 ± 0.4 | 26.9 ± 0.7* | 45.4 ± 0.6* | 54.6 ± 0.9 | 81.5 ± 0.2* | 3.3 |
| SciBERT | No augmentation | 68.1 ± 0.6 | 72.7 ± 0.3 | 77.3 ± 0.5 | 79.8 ± 0.7 | 35.1 ± 1.1 | 62.7 ± 1.5 | 70.2 ± 0.3 | 87.8 ± 0.2 | |
| SciBERT | Label-wise token rep. | 70.0 ± 0.8* | 72.8 ± 0.2 | 76.0 ± 0.6 | 80.2 ± 0.6 | 39.3 ± 1.7* | 64.8 ± 1.3 | 71.2 ± 0.4* | 87.5 ± 0.2 | 1.0 |
| SciBERT | Synonym replacement | 70.6 ± 1.2* | 73.9 ± 0.1* | 76.8 ± 0.4 | 79.7 ± 0.5 | 42.3 ± 1.3* | 65.3 ± 0.3* | 70.5 ± 2.3 | 87.7 ± 0.4 | 1.6 |
| SciBERT | Mention replacement | 70.5 ± 0.8* | 73.3 ± 0.4 | 76.7 ± 0.7 | 80.0 ± 0.3 | 40.1 ± 2.5* | 64.2 ± 1.2 | 70.8 ± 0.7 | 87.8 ± 0.2 | 1.2 |
| SciBERT | Shuffle within segments | 70.5 ± 0.4* | 73.1 ± 0.6 | 76.7 ± 0.3 | 80.3 ± 0.5 | 39.4 ± 1.6* | 63.9 ± 1.4 | 71.2 ± 1.2 | 87.7 ± 0.2 | 1.1 |
| SciBERT | All | 71.2 ± 0.8* | 73.1 ± 0.6 | 76.9 ± 0.4 | 80.5 ± 0.4 | 41.5 ± 0.9* | 65.2 ± 0.3* | 72.3 ± 1.3* | 87.2 ± 0.3 | 1.8 |
Headline reading the paper gives §4.4:
- All methods beat the baseline on average, with no single winner across both models. Synonym replacement is the best single method on average for SciBERT; mention replacement is the best single method on average for the LSTM §4.4 tab. 3.
- Applying all methods together beats any single method on average, but on the full training set a single method is sometimes better (MaSciP LSTM, i2b2 SciBERT) §4.4 tab. 3.
- Gains are concentrated in the small sets, and all four single methods are significant there for both models and both data sets §4.4 tab. 3.
- The authors attribute the weaker effect of combining methods on large sets to a trade-off between diversity and label validity §4.4.
- The authors note significant improvements with pretrained transformers and argue that augmentation should also be studied on pretrained models such as BERT §4.4.
Paper vs code
Code paths are relative to the repository at code_commit (raw/code/2010.11683). The repository is the official release (boschresearch/data-augmentation-coling2020, Apache-2.0); its README names the paper and both authors README.md.
- LSTM encoder is missing. The paper's LSTM tagger §4.2 has no counterpart in the code: the encoder asserts
embedding_type == "bert"models.py, and the README runs only the BERT path README.md. Only the SciBERT results can be reproduced from this release. - Optimiser and schedule. The paper gives none §4.3. The code uses AdamW (named "adam") at 3e-5, batch 16, up to 100 epochs, a reduce-on-plateau schedule and early stopping main.py train.py.
- One learning rate for everything. The encoder, the output layer and the CRF share one optimiser at one rate train.py. The paper says nothing about per-part rates.
- Gradient clipping per module. Norm 5.0 is applied to the encoder, the output layer and the CRF separately train.py, not to the whole model. The paper does not mention clipping.
- Output layer. The code puts a hidden linear layer of the BERT width and an output linear layer with no activation between them, then a linear-chain CRF main.py models.py. The paper describes the CRF but not this extra layer §4.2.
- CRF transition constraints. Viterbi decoding forbids invalid BIO transitions models.py models.py. Training uses the unconstrained transition matrix models.py. The paper mentions neither.
- Output label set. The tag dictionary is built from the train, dev and test sets data.py main.py. The paper does not say this.
- Augmentation timing. The paper does not say when augmented instances are made. The code regenerates them at every epoch from the original sentences, with fresh random draws train.py train.py.
- Stopwords. The code never replaces NLTK English stopwords in LwTR or SR augment.py augment.py augment.py augment.py. The paper does not mention stopwords.
- Smoothing exponent. The code exposes an exponent on the LwTR token counts, default 1.0, which leaves the distribution unchanged main.py augment.py. The paper does not describe it.
- Replace probability and instance count. The paper tunes them on dev §4.3. The code defaults to 0.3 and 1 main.py and has no search loop, so the grid must be run externally.
- Subset construction and dev split. The paper's training subsets and 15% dev split §4.1 are not in the code. The README says to copy prepared files into
data/README.md;data/holds only sample files train.txt. - Metric. The code matches spans on label, text and token indices train.py data.py. A span with the right text at a different position is a miss. The paper says "string match" without this detail §4.2.
- Shipped result files are not the paper's numbers.
results/*.jsonrecord--data_folder data, the folder that holds only sample files all.json train.txt. The README table (F1 37.9 for no augmentation) has no stated setup and does not match tab. 3 README.md. - Unused flag.
--variational_dropoutis parsed but not used in the files read main.py. - Model selection. Best-dev checkpoint, then test: the code matches the paper train.py train.py.
Pitfalls
- Many settings are unstated. Optimiser, learning rate, batch size, epochs, dropout, clipping and seeds are missing from the paper §4.3. The code's defaults are the only concrete values, so use them as the reference and say so when you deviate.
- Keep the label set consistent. The paper says a replacement keeps the label. In code, LwTR draws from a distribution keyed by the full BIO tag, so B-type and I-type tokens have separate pools augment.py. Keep that split, or an I- token can be replaced by a B- token.
- Synonym choice depends on set order. SR picks from
list(set)augment.py, so the same seed can pick different synonyms across processes unless you sort the candidates first. Sorting is a suggestion for reproducibility, not something the paper or code does. - SR pools all senses. Lemma names from every WordNet sense of the word are pooled augment.py, and the paper does not say whether senses are filtered by part of speech.
- MR can redraw the same mention. Candidates come from the whole training set and may include the original mention augment.py.
- SiS moves words inside a mention. Mention segments are shuffled too, so "neuropathic pain syndrome" can become "pain neuropathic syndrome" with labels B I I §3 augment.py. The label row is kept, but the entity text changes.
- No truncation. The encoder does not truncate inputs models.py. Sentences longer than the BERT input limit are not handled in the code read.
- Word dropout without rescaling. Whole token vectors are zeroed with a Bernoulli mask and not rescaled models.py.
- Dev and test labels in the output space. The tag set includes labels that appear only in dev or test data.py. For strict hygiene, build it from train only.
- The text is more positive than the table. The text says all methods improve on the baseline §4.4. On the full set, several single-method cells are equal or lower: SciBERT i2b2 F is 87.5 (LwTR), 87.7 (SR), 87.8 (MR), 87.7 (SiS) and 87.2 (All) against 87.8 for no augmentation; LSTM MaSciP F is 76.3 (LwTR) and 76.0 (MR) against 76.4 tab. 3. Treat the F column as no reliable gain.
- Wide spreads on small sets. Some cells have large standard deviations. The SciBERT i2b2 L cell for synonym replacement is 70.5 ± 2.3 against 70.2 ± 0.3 with no augmentation and is not underlined tab. 3. With five seeds, compare methods only within one recipe.
- Data access. Neither data set ships with the code. The paper links portals for both §4.1. Access terms are not stated here.
- The compiled analysis is excluded. The source has an analysis section that is commented out (
%\input{7-analysis}) and does not appear in the compiled paper. Its tables are not summarised here. - Figures. The source's figure is inside a disabled block, so the card cites tables only.
Lineage
- Builds on: word replacement, mention replacement and token swaps from sentence-level NLP work, which the paper surveys §2. The paper adapts them to token labels and drops methods that need external models §3.
- Compared against: the no-augmentation baseline for each model and data set tab. 3.
Source and license
Adapted from “An Analysis of Simple Data Augmentation for Named Entity Recognition” by Xiang Dai, Heike Adel (arXiv:2010.11683), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/boschresearch/data-augmentation-coling2020 at commit f4431dd1df26 (Apache-2.0). Any quoted code is shown under that license.