Labeling text is expensive. Active learning lets you focus annotation
effort on the examples a model is most uncertain about, so each
label has maximum impact on performance. conflibertR
exposes this as a small, intuitive loop:
The package bundles a small demo dataset: a 20-text labeled seed, a 61-text unlabeled pool, and a dev set. It also includes oracle labels for the pool so you can simulate a full loop without a human in the loop (for testing only; don’t use the oracle in real workflows).
conflibert_active_start() trains a classifier on the
seed and returns a session object containing the first uncertain batch.
Each round, pass the session to conflibert_active_next()
along with your labels.
session <- conflibert_active_start(
seed = demo$seed,
pool = demo$pool,
dev = demo$dev,
model = "ConfliBERT",
task = "binary",
strategy = "entropy", # or "margin", "least_confidence"
query_size = 10,
epochs = 1
)
sessionThe session’s $query is a tibble of texts to label next,
with an uncertainty column showing how unsure the model is.
$metrics tracks scores across rounds;
$labeled_n / $pool_n track progress.
For real labeling, the easiest route is the built-in Shiny gadget. It opens a modal dialog (or browser tab) showing every row of the current query with radio buttons for each class: click, submit, done.
labels <- conflibert_active_label(session)
session <- conflibert_active_next(session, labels = labels)You can also provide the labels by hand (useful for scripting, or if you prefer a console-only workflow):
# labels in the same order as session$query
labels <- c(1, 0, 1, 0, 0, 1, 0, 1, 0, 1)
session <- conflibert_active_next(session, labels = labels)For this vignette we use the bundled oracle to simulate labeling:
labels <- unname(demo$pool_labels[session$query$text])
session <- conflibert_active_next(session, labels = labels)
sessionRepeat until the pool is exhausted or the learning curve flattens. Here’s a short simulation loop:
plot() produces a two-panel diagnostic: the learning
curve on top (metrics vs labeled-set size) and the query uncertainty
trend on the bottom. When mean uncertainty flattens, the model is no
longer finding informative samples, a good signal to stop.
Three uncertainty strategies are available. Pass one via
strategy:
"entropy" (default): highest Shannon entropy of the
predicted class distribution. Works well for both binary and
multiclass."margin": smallest gap between the top two class
probabilities. Targets ambiguous samples on the decision boundary."least_confidence": lowest maximum class probability.
Simplest strategy; a good baseline.Pure uncertainty sampling can pick several near-duplicates in one
batch, a problem when your pool has many similar texts. Pass
diverse = TRUE to cluster the top-scoring candidates in the
model’s embedding space and pick the highest-scoring sample from each
cluster:
For bigger base models or tighter GPU budgets, train only a low-rank adapter each round. The adapter is merged into the base model before every round ends, so scoring, saving, and reloading behave exactly like full fine-tuning:
Persist the final model as a standard HuggingFace checkpoint:
You can reload it with any transformers tool, or point
AutoModelForSequenceClassification.from_pretrained() at the
directory from Python.
conflibert_active_start() and reused for every
subsequent round.saveRDS() on the session won’t serialize the model; use
conflibert_active_save() to persist it, and re-run rounds
from a fresh session if needed.