Efficient Few-Shot Learning with SetFit from Hugging Face
SetFit trains a classifier from 16 labelled examples in under a minute. On one GLUE task that beats the baseline by 21 points; on another it loses to always guessing.
Machine Learning
NLP
Author
Ravi Kalia
Published
April 4, 2025
Efficient Few-Shot Learning with SetFit from Hugging Face
Sixteen labelled examples and about twenty seconds of CPU time will give you a working text classifier. That is SetFit’s claim and it holds up — but the number it produces depends far more on which task you point it at than on anything you configure, and the failure is quiet.
Below, identical code with an identical budget scores 0.71 on sentiment, twenty-one points clear of chance, and 0.59 on paraphrase detection, which is worse than always guessing the majority class. Neither run errors or warns. Understanding why is the difference between using SetFit well and being fooled by it.
SetFit is a frozen encoder plus a logistic regression
The method has two stages and no language-model fine-tuning at all. First, a sentence-transformer is adapted with contrastive learning: from your few labelled examples it generates many pairs, labelled “same class” or “different class”, and nudges the embedding so same-class sentences sit closer together. Then a plain logistic-regression head is fitted on the resulting embeddings.
That is where the efficiency comes from. Pair generation turns 16 examples into hundreds of training signals, and the classifier is fitting a few hundred parameters over a 384-dimensional vector rather than fine-tuning 100M weights. No prompts, no generation, no GPU required.
It also predicts exactly where the method will fail. If two classes are not already separable — or nearly so — in the encoder’s embedding space, a small contrastive nudge and a linear head will not put them there.
The data, and what is being asked of it
Both tasks come from GLUE (Wang et al., 2018), a benchmark assembled to measure general language understanding across nine tasks, distributed as nyu-mll/glue. Note the namespace: datasets 5.x requires namespace/name and the bare "glue" alias now raises HfUriError.
MRPC — the Microsoft Research Paraphrase Corpus, 3,668 training pairs of news sentences, each labelled by human annotators as paraphrases or not. It was built from a news crawl to study whether systems can recognise that two differently-worded sentences mean the same thing.
SST-2 — the Stanford Sentiment Treebank, movie-review sentences labelled positive or negative, collected to study compositional sentiment.
The question here is narrower than either benchmark’s: given only 8 labelled examples per class, can we build a usable classifier at all? That is the realistic situation when labelling is expensive — a domain expert can annotate a handful of examples, not thousands.
What it would cost to be wrong depends on the deployment. A paraphrase detector wrong in the permissive direction silently merges distinct claims, which in a deduplication or plagiarism setting is the expensive error. Neither model below is fit for either purpose; the point is the method, and the comparison is what makes the method legible.
Sentiment: the case that works
Load the corpus, take eight examples per class, train, evaluate. sample_dataset is SetFit’s helper for the stratified few-shot sample — taking a head slice instead would risk the single-class trap that ordered corpora set:
model_head.pkl not found on HuggingFace Hub, initialising classification head with random weights. You should TRAIN this model on a downstream task to use it for predictions and inference.
Applying column mapping to the training dataset
Applying column mapping to the evaluation dataset
***** Running training *****
Num unique pairs = 160
Batch size = 16
Num epochs = 1
[10/10 00:01, Epoch 1/1]
Step
Training Loss
1
0.368900
***** Running evaluation *****
accuracy 0.710 vs majority baseline 0.500
Around 0.71 against a 0.5 baseline, from sixteen examples in seconds. This is the result SetFit is advertised on, and it is real: sentiment is close to a direction in sentence-embedding space, because the encoder’s pretraining data already made positive and negative language look different.
column_mapping is the piece worth noting — SetFit expects columns named text and label, and this maps GLUE’s sentence onto that without copying the dataset.
Paraphrase: identical code, worse than guessing
MRPC is a sentence pair task, so the two sentences are joined into one string before encoding. Everything else is unchanged:
model_head.pkl not found on HuggingFace Hub, initialising classification head with random weights. You should TRAIN this model on a downstream task to use it for predictions and inference.
Applying column mapping to the training dataset
Applying column mapping to the evaluation dataset
***** Running training *****
Num unique pairs = 160
Batch size = 16
Num epochs = 1
[10/10 00:02, Epoch 1/1]
Step
Training Loss
1
0.531300
***** Running evaluation *****
accuracy 0.590 vs majority baseline 0.685
beats baseline: False
Roughly 0.59 against a 0.685 baseline. A model that ignored the input entirely and always answered “paraphrase” would have done better.
Two things caused this, and they compound. MRPC’s validation split is 68% positive, so accuracy on it is a soft target that a constant predictor already hits — the metric flatters the trivial solution. And more fundamentally, paraphrase-equivalence is not a direction in embedding space: two sentences about the same news event embed close together whether or not they are paraphrases, because sentence encoders are trained to place topically similar text nearby. The signal SetFit needs is close to orthogonal to the signal the encoder provides.
Caveat: this is not a verdict on SetFit or on MRPC
One seed, one sample of 8 per class, one evaluation subset. Few-shot results have large variance across seeds — the sample you happen to draw matters enormously at this size — so the honest reading is “this configuration failed”, not “SetFit cannot do MRPC”. With a few hundred labels per class, or a cross-encoder built for sentence pairs, MRPC is very learnable.
The transferable claim is narrower and safer: always compute the majority baseline. An accuracy of 0.59 looks like learning until you know that 0.685 was free.
Comparing the two runs
Side by side, with everything except the task held constant:
Same encoder, same 16 examples, same hyperparameters, same seconds of compute. The only variable is whether the task’s decision boundary was already latent in the embedding, and it accounts for the entire difference between a useful classifier and a harmful one.
Predicting, and what the model actually is
A trained model predicts from raw strings, and is small enough to be worth looking at:
positive an absolute delight from start to finish
positive a tedious, joyless slog
head: LogisticRegression
body: SentenceTransformer
The second one is wrong, and it is worth leaving in. “A tedious, joyless slog” is about as negative as film criticism gets, and the model calls it positive — which is what an accuracy of 0.71 means in practice. Roughly three in ten, and there is no reason the three should be the ambiguous ones. A number that reads as respectable in a table is still a model that will confidently misclassify plain cases, and sixteen training examples buy exactly that much.
The architecture explains how it got here: a LogisticRegression sitting on a SentenceTransformer. The head has seen sixteen points in 384 dimensions, so its decision boundary is determined by very little, and where the encoder’s geometry does not already separate the classes it has nothing else to go on. That also means the head is inspectable and swappable — SetFitModel.from_pretrained(..., use_differentiable_head=True) substitutes a torch head you can train jointly.
Where few-shot is the right tool
The claim at the top was that SetFit’s output depends more on the task than on the configuration. The evidence is two runs differing only in which dataset they loaded, landing 21 points above and 9 points below their respective baselines.
So the question to ask before reaching for it is not “do I have few labels” but “does a general-purpose sentence encoder already place my classes apart”. Topic, sentiment, intent and tone usually qualify — they are properties of what the sentence is about, which is what these encoders were built to represent. Relations between sentences, fine distinctions inside a specialist vocabulary, and anything requiring the model to reason rather than recognise usually do not.
When the answer is no, more few-shot examples will not rescue it, because the limitation is in the frozen encoder rather than in the sixteen labels. That is the point at which the cheap method has told you something useful: you need a different representation, not a bigger sample.