Paper · 2021-09-08 · cs.CL, cs.AI, cs.LG
Active Learning by Acquiring Contrastive Examples
CAL selects unlabeled examples whose predicted class distribution differs most from those of their k nearest labeled neighbours in BERT [CLS] space, scored by mean KL divergence. Over four NLU tasks and seven datasets (BERT-base, 15% budget) the authors report it is best on two of three out-of-domain columns and top-performing in-domain, shown only as curves.
Active learningUncertainty estimationNearest-neighbor searchactive-learningdata-selectiontext-classificationbert
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-haiku-5-5 on 2026-11-06, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Labeling data costs time and money, so active learning asks which unlabeled examples are worth labeling next. Two common answers are to pick the examples the model is least sure about, or to pick a spread of examples that covers the data. Both have a known weakness: uncertain picks can be repetitive, and spread-out picks can be too easy for the model.
This paper proposes a different choice, called CAL. It looks for examples that the model places near examples it has already labeled, but that the model predicts very differently from those neighbours. The authors expect such examples to sit close to the line where the model changes its answer, which is where a new label helps most.
The authors test CAL on sentiment, topic, language-inference and paraphrase tasks with a BERT model. They report that it matches or beats the other strategies they compared, including on data from a different source than the training data.
TL;DR
- Active learning picks which unlabeled examples to label next; CAL picks ones that look similar to labeled data in the model's feature space but get very different predicted probabilities §2.1.
- Score: for each pool example, average the KL divergence between the predicted distribution of each of its k nearest labeled neighbours and its own §2.2 alg. 1.
- Author-reported: best out-of-domain accuracy on SST-2→IMDb (79.00 ± 1.39) and QQP→TwitterPPDB (86.20 ± 0.22); on IMDb→SST-2 Entropy is best (85.54 vs 84.96) tab. 2.
- In-domain results are shown only as curves fig. 2; the ablations are shown only as curves on AG News fig. 3.
- Costs more than entropy sampling: one extra pass over the labeled set plus a neighbour search each round app. A.2 tab. 4.
Problem
Pool-based active learning labels data in rounds: train a model on the labeled set, score the unlabeled pool, label a batch, repeat until a budget is spent §2.2. The two dominant score families have known limits. Uncertainty-based scores can select uncertain but redundant points, and diversity-based scores can select diverse but easy points §1. Hybrid methods that cluster uncertainty-derived representations (BADGE, BertKM) are described as computationally heavy, with cost growing with the acquisition size §3.2, footnote app. A.2.
The method assumes a simulated loop: the pool's labels are known and revealed only when an example is acquired §2.2, footnote. Models are BERT-base with a task-specific classification layer §3.3.
Method
CAL scores each unlabeled example by how strongly its predicted class distribution disagrees with those of its nearest labeled neighbours, measured in the model's own [CLS] feature space. The "neighbourhood" is the top-k labeled examples, not a distance threshold §2.2. The score is a mean of KL divergences, and the batch is the b highest-scoring pool examples alg. 1.
- Fine-tune BERT-base with a classification layer on the current labeled set D_lab §2.2 §3.3.
- Represent every labeled and pool example by its [CLS] embedding Φ(·) §2.2.
- For each pool example x_p, find the k nearest labeled examples in Euclidean distance; k is fixed, not thresholded §2.2.
- Compute the predicted distributions p(y|x) of the neighbours and of x_p with the current model §2.2 alg. 1.
- Score x_p as the mean over neighbours of KL(p(y|x_l) ‖ p(y|x_p)); the neighbour's distribution comes first §2.1, footnote alg. 1.
- Select the b pool examples with the largest scores; reveal their labels, move them to D_lab, and repeat §2.2 alg. 1.
The paper defines contrastive examples with two criteria, a similarity bound and a divergence condition eq. 1 eq. 2. The bound ε in eq. 1 is replaced in practice by a fixed k nearest neighbours §2.2, and the divergence condition in eq. 2 is realised as the mean KL score in step 5 alg. 1.
d\big(\Phi(x_i),\Phi(x_j)\big) < \epsilon eq. 1
\mathrm{KL}\big(p(y|x_i)\,\|\,p(y|x_j)\big) \rightarrow \infty eq. 2
Selection for one round, as the paper's Algorithm 1 gives it, in our own pseudocode. The paper's KL is written as KL(neighbour ‖ candidate) §2.1.
# CAL: score each pool example, keep the top b [alg. 1] [§2.2]
# Input: labeled set (m examples), pool (n examples), batch size b,
# fine-tuned model, neighbour count k, encoder phi (the [CLS] vector)
# Output: indices of b pool examples
def cal_select(labeled, pool, b, model, k, phi):
# Embed labeled and pool examples with the model's [CLS] vector [§2.2]
labeled_emb = phi(labeled) # (m, d)
pool_emb = phi(pool) # (n, d)
# Predicted class distributions of labeled and pool examples [alg. 1]
labeled_prob = softmax(model(labeled)) # (m, C)
pool_prob = softmax(model(pool)) # (n, C)
scores = []
for pool_index in range(n):
# The k labeled examples nearest in Euclidean distance [§2.2]
neighbours = k_nearest(pool_emb[pool_index], labeled_emb, k)
# KL(neighbour || candidate) for each neighbour [§2.1] [alg. 1]
kl_values = []
for labeled_index in neighbours:
kl_values.append(kl(labeled_prob[labeled_index],
pool_prob[pool_index]))
# Mean over the neighbourhood is the score [alg. 1] [§5]
scores.append(mean(kl_values))
# The b largest scores form the batch [alg. 1]
return top_b_indices(scores, b)The surrounding loop, with the simulated labels. Starting-set and budget rules are in §2.2 and §3.3.
# Simulated active learning loop [§2.2] [§3.3]
# Pool labels exist but are hidden until acquired.
def run_active_learning(train_set, total_budget, b, init_size, seed):
# Random label-stratified initial set [§2.2] [§3.3]
labeled = stratified_sample(train_set, init_size, seed)
pool = train_set minus labeled
while len(labeled) < total_budget:
# Start each round from the pretrained checkpoint, not the last model
# [code: utilities/trainers.py#L828-L833]
model = fine_tune_pretrained_bert(labeled)
# Score the pool and select a batch [alg. 1]
batch = cal_select(labeled, pool, b, model, k, phi)
# Reveal labels and move the batch into the labeled set [§2.2]
labeled = labeled + batch
pool = pool minus batch
return labeledOther measures used in the analysis are described in words, with their formulas, in §6.1: an input-space diversity (Jaccard overlap of token sets), a feature-space diversity (inverse mean nearest-neighbour distance of [CLS] vectors), uncertainty (mean predictive entropy of a fully trained model), and representativeness (inverse mean KNN distance with K = 10).
Hyperparameters
Values stated in the paper are cited to the paper. Values taken only from the official code are marked "code default" or "code only" and cited to the code. The paper states no value for epochs, warmup, gradient clipping or early stopping.
| Name | Value | Where | Notes |
|---|---|---|---|
| Encoder | BERT-base with a task-specific linear classification layer | §3.3 | Cased/uncased not stated. Code default bert-base-cased run_al.py |
| Optimizer | AdamW, epsilon 1e-8, weight decay 0 | app. A.1 | trainers.py run_al.py |
| Learning rate | 2e-5 | app. A.1 | Schedule not stated in paper; code uses linear decay trainers.py |
| Batch size | 16 | app. A.1 | run_al.py |
| Warmup | not stated | — | Code only: 10% of total steps trainers.py |
| Epochs | not stated | — | Code default 3 run_al.py |
| Gradient clipping | not stated | — | Code default max norm 1.0 run_al.py |
| Max sequence length | 128; 256 for IMDb | app. A.1 | Code default 256 for all datasets run_al.py. See Paper vs code |
| Validation | Sampled from train: 10% for QNLI, IMDb, SST-2; 5% for AG News and QQP; DBpedia undersampled | app. A.1 | Code only: DBpedia to 20K train and 2K val, stratified, seed 42 run_al.py |
| Evaluation | 5 times per epoch; keep the checkpoint with the lowest validation loss | §3.3 app. A.1 | Code only: interval capped at 500 steps trainers.py; selection on loss trainers.py |
| Early stopping | not stated | — | Code only: off unless --patience is set run_al.py |
| Seeds | 5 random seeds, drawn from [1, 9999] | app. A.1 | Exact seeds not stated |
| Initial labeled set | 1% of training data, random, label-stratified | §2.2 §3.3 | Code: run_al.py |
| Acquisition size b | 2% | §3.3 | Paper gives no base; code takes 2% of the full training set. See Paper vs code |
| Budget | 15% | §3.3 §4.2 | Paper says "of D_pool"; code takes 15% of the full training set run_al.py |
| Neighbours k | not stated | §2.2 | Code default 10 run_al.py; the paper's K = 10 in §6.1 is a different metric |
| Distance | Euclidean | §2.2 | Code also L2-normalises both sets first cal.py |
| Neighbourhood operator | mean | alg. 1 §5 | Code default mean run_al.py; max and median were tested and perform similarly §5 |
| Score | KL(neighbour ‖ candidate) | §2.1 alg. 1 | Cross-entropy with gold labels is an ablation §5 |
| Confidence masking | none | — | Code default off run_al.py |
| Hardware | one NVIDIA Tesla V100 per experiment | §3.3 | — |
| Test sets | Standard test set; for SST-2, QNLI and QQP the GLUE dev set is the held-out test set | app. A.1 | — |
| OOD pairs | SST-2 ↔ IMDb; QQP → TwitterPPDB | §3.1 | — |
Results
Author-reported. Setup: BERT-base; seven datasets for four tasks tab. 1; five seeds app. A.1; out-of-domain (OOD) accuracy is measured on the models trained on the final acquired dataset (15% of the pool) §4.2. The paper does not define the ± value in the table caption.
Dataset statistics as the paper gives them tab. 1. DBpedia counts are after undersampling; the original DBpedia training set has 560K examples app. A.1.
| Name | Kind of task | Text source | Shifted test set | Train | Val | Test | Labels |
|---|---|---|---|---|---|---|---|
| IMDb | Sentiment (binary) | Film reviews | SST-2 | 22.5K | 2.5K | 25K | 2 |
| SST-2 | Sentiment (binary) | Film reviews | IMDb | 60.6K | 6.7K | 871 | 2 |
| AGNews | News category | News articles | — | 114K | 6K | 7.6K | 4 |
| DBpedia | News category | Encyclopedia entries | — | 20K | 2K | 70K | 14 |
| PubMed | Medical category | Medical abstracts | — | 180K | 30.2K | 30.1K | 5 |
| QNLI | Entailment (binary) | Wikipedia sentence pairs | — | 99.5K | 5.2K | 5.5K | 2 |
| QQP | Duplicate detection | Community questions | TwitterPPDB | 327K | 36.4K | 80.8K | 2 |
Out-of-domain accuracy of models trained on the datasets acquired by each strategy tab. 2. Each column trains on the first named dataset and tests on the second. Bold marks the best value per column, as in the paper.
| Strategy | SST-2 → IMDb | IMDb → SST-2 | QQP → TwitterPPDB |
|---|---|---|---|
| Random | 76.28 ± 0.72 | 82.50 ± 3.61 | 85.86 ± 0.48 |
| BertKM | 75.99 ± 1.01 | 84.98 ± 1.22 | — |
| Entropy | 75.38 ± 2.04 | 85.54 ± 2.52 | 85.06 ± 1.96 |
| ALPS | 77.06 ± 0.78 | 83.65 ± 3.17 | 84.79 ± 0.49 |
| BADGE | 76.41 ± 0.92 | 85.19 ± 3.01 | — |
| CAL | 79.00 ± 1.39 | 84.96 ± 2.36 | 86.20 ± 0.22 |
Headline statements in the text, all author-reported:
- Out-of-domain: CAL gives the highest SST-2→IMDb accuracy "by a large margin", and the highest QQP→TwitterPPDB accuracy §4.2.
- The authors say CAL is "consistently similar or better" than the other approaches on out-of-domain data §4.2. The table shows Entropy above CAL on IMDb→SST-2, within the spread of both.
- In-domain: CAL is said to be top-performing, especially on DBpedia, PubMed and AG News; Entropy is second overall; BertKM is competitive on SST-2 and QNLI but always below CAL and Entropy; BADGE is described as low; ALPS is close to random except on SST-2 §4.1 fig. 2. The numbers are only in fig. 2.
- The abstract and conclusion say CAL is "better or equal" to the best baseline in all tasks §1 §8. The in-domain claim has no table in the paper.
Analysis, averaged over datasets, on each strategy's full acquired set at 15% of the pool tab. 3 §6.1:
| Strategy | Div.-I | Div.-F | Unc. | Repr. |
|---|---|---|---|---|
| Random | 0.766 | 0.356 | 0.132 | 1.848 |
| BertKM | 0.717 | 0.363 | 0.145 | 2.062 |
| Entropy | 0.754 | 0.323 | 0.240 | 2.442 |
| ALPS | 0.771 | 0.360 | 0.126 | 2.038 |
| BADGE | 0.655 | 0.339 | 0.123 | 2.013 |
| CAL | 0.768 | 0.335 | 0.231 | 2.693 |
The paper reads these as: CAL and Entropy acquire the most uncertain data; CAL acquires the most representative data; ALPS acquires the most diverse input text §6.2.
Ablations on AG News, in-domain accuracy fig. 3 §5, all described in words:
- Choosing the least-divergent candidates instead (argmin) leaves accuracy unchanged or lower than the first model: evidence that the ranking carries signal.
- Building neighbourhoods from each labeled example instead of each pool example is slightly worse.
- Cross-entropy with gold labels instead of KL is slightly worse.
- Max and median in place of the mean perform similarly to the mean.
- The [CLS] vector is the best feature space; mean embeddings, mean outputs, pretrained-BERT [CLS] and TF-IDF are worse.
Efficiency app. A.2 tab. 4 tab. 5. Runtimes in seconds, one V100. Each cell in tab. 4 is (inference, selection); the values are medians, because the paper could not report every round's runtime tab. 4 caption. Averages in tab. 5 are over all datasets (avg-all), the first three (avg-3), or all except QQP (avg-6).
| Strategy | DBpedia | IMDb | SST-2 | QNLI | AG | PubMed | QQP |
|---|---|---|---|---|---|---|---|
| Random | (0,0) | (0,0) | (0,0) | (0,0) | (0,0) | (0,0) | (0,0) |
| ALPS | (0,181) | (0,222) | (0,733) | (0,1607) | (0,2309) | (0,5878) | (0,14722) |
| BertKM | (0,467) | (0,431) | (0,4265) | (0,8138) | (0,9344) | (0,25965) | (–,–) |
| BADGE | (0,12871) | (0,3816) | (0,25640) | (–,–) | (–,–) | (–,–) | (–,–) |
| Entropy | (103,1) | (107,0) | (173,0) | (331,0) | (402,0) | (596,0) | (1070,0) |
| CAL | (133,49) | (212,61) | (464,244) | (528,376) | (656,628) | (1184,1445) | (1541,2857) |
| Strategy | DBpedia | IMDb | SST-2 | QNLI | AG | PubMed | QQP | avg-all | avg-3 | avg-6 |
|---|---|---|---|---|---|---|---|---|---|---|
| Random | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| ALPS | 181 | 222 | 733 | 1607 | 2309 | 5878 | 14722 | 3664 | 378 | 1821 |
| BertKM | 467 | 431 | 4265 | 8138 | 9344 | 25965 | – | – | 1721 | 8101 |
| BADGE | 12871 | 3816 | 25640 | – | – | – | – | – | 14109 | – |
| Entropy | 104 | 107 | 173 | 331 | 402 | 596 | 1070 | 397 | 128 | 285 |
| CAL | 182 | 273 | 708 | 904 | 1284 | 2629 | 4398 | 1482 | 387 | 996 |
The text says CAL's inference cost is higher than Entropy's because it needs a forward pass over both the labeled and the pool sets, and that its selection step is cheaper than inference in most cases app. A.2.
Paper vs code
The official repository is mourga/contrastive-active-learning at the commit
above. The paper links it §1, footnote. It is GPL-3.0 licensed, so this card cites the code and writes its
own pseudocode; it quotes no code. Files used: acquisition/cal.py,
run_al.py, utilities/trainers.py.
- Distance space. The paper says Euclidean distance on [CLS] vectors §2.2. The code L2-normalises the labeled and pool embeddings first cal.py. For unit-length vectors, Euclidean ranking equals cosine ranking, so the neighbours can differ from the paper's stated method.
- Model at each round. The paper says the model is "fine-tuned at each AL iteration using the current D_lab" §2.2. The code builds a new model from the pretrained checkpoint on every round trainers.py. The paper's wording does not say which.
- Percentages. The paper gives the budget as 15% "of D_pool" §3.3. The code computes the budget, the initial set and the acquisition size as percentages of the full training set after DBpedia undersampling run_al.py run_al.py. The difference is small on the first round but changes the absolute counts.
- Acquisition size. The paper gives b = 2% §3.3. The complexity section uses k = 100 as the acquisition size app. A.2 and gives QQP's acquisition size as 3270 app. A.2. 3270 is 1% of the QQP training count in tab. 1 (327K), not 2%. The paper does not reconcile these.
- Max sequence length. The paper says 128, and 256 for IMDb app. A.1. The code default is 256 for every dataset run_al.py, and the README's only example sets no length [README.md]. The paper's runs cannot be pinned to one value from the code alone.
- Neighbour count. The paper never gives k for CAL. The code default is 10 run_al.py. Treat 10 as code only.
- Evaluation frequency. The paper says 5 evaluations per epoch §3.3. The code computes the interval as one fifth of the epoch in steps, capped at 500 trainers.py. Above about 40K labeled examples (2,500 steps per epoch at batch 16) the cap gives more than 5 per epoch.
- Retry branch. The code can retrain from scratch up to three times when validation accuracy drops by more than 0.5 trainers.py. Accuracy is a fraction in this code, so the branch almost never fires. The paper does not mention it.
- Ablation path. The labeled-centroid ablation (
--knn_lab) callsfaiss, but its import is commented out cal.py and it is used at cal.py. As shipped, that path cannot run.
No discrepancies were found in the KL direction, the top-b selection, or the mean aggregation: the code matches alg. 1 and §2.1.
Pitfalls
- KL direction. The score is KL(neighbour ‖ candidate). Swapping the arguments changes the ranking. The code uses the paper's order cal.py.
- Unbounded scores. A neighbour's probability mass on a class the candidate almost rules out makes the KL term very large, because the candidate's log-probabilities are unbounded below. A few confident-but-different candidates can dominate the top b. The paper's ablations do not test this.
- Ties. The top-b choice uses an argpartition, so among equal scores which examples are picked is arbitrary cal.py.
- Batch independence. Each candidate is scored on its own; nothing penalises picking near-duplicates within one batch. The paper does not discuss this.
- Cost grows with the labeled set. Each round needs a forward pass over the labeled set and the pool, plus a neighbour search over the labeled set app. A.2. The paper calls the cost linear in pool size app. A.2; the timings are on one V100 and may not transfer.
- Labels are simulated. The pool's labels exist and are hidden until an example is acquired §2.2, footnote. Human-labeling effects are not studied.
- Gold labels are not used by default. The default score uses the model's predicted distributions of the neighbours, not their gold labels. The gold-label variant is an ablation §5.
- Variance is unclear. The ± values are not defined in the table caption, and the paper gives only five seeds app. A.1. Overlapping ± ranges mean a claim of "best" is weak evidence.
- Wording vs numbers. The abstract and conclusion say "better or equal" in all tasks §1 §8. Table 2 has Entropy above CAL on IMDb→SST-2, and the in-domain claims rest on a figure.
- Table 1 counts. The DBpedia train and validation counts are after undersampling, not the original DBpedia split app. A.1.
- Overloaded k. The complexity section uses k for the acquisition size app. A.2, while Algorithm 1 uses k for the neighbour count alg. 1. Keep them apart in an implementation.
- Fresh vs continued training. The paper's "fine-tune at each iteration" can be read as continuing from the previous model. The code restarts from the pretrained checkpoint every round trainers.py. Results will differ between these.
Lineage
- Builds on: uncertainty sampling, least confidence and predictive entropy §7; contrastive learning is cited as the inspiration for pairing similar inputs with different outputs §2.1.
- Compared against: Random, Entropy, BertKM and BADGE (Ash et al., 2020), and ALPS (Yuan et al., 2020) §3.2.
- Superseded by: not stated in this paper; no later work is linked from this wiki.
Source and license
Adapted from “Active Learning by Acquiring Contrastive Examples” by Katerina Margatina, Giorgos Vernikos, Loïc Barrault, Nikolaos Aletras (arXiv:2109.03764), 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/mourga/contrastive-active-learning at commit aeb7cd90ce00 (GPL-3.0). Any quoted code is shown under that license.