wikillm

Paper · 2021-10-14 · cs.CL

P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks

P-tuning v2 adds trainable prompt vectors to every layer of a frozen pretrained model, plus a trainable classification head. Author-reported: comparable to fine-tuning on SuperGLUE dev from BERT-large to GLM-10B with 0.1-3% trainable parameters, and competitive on sequence labeling and extractive QA, where earlier prompt tuning lagged far behind.

Soft prompt tuningParameter-efficient fine-tuningSequence labelingExtractive question answeringsoft-prompt-tuningparameter-efficient-fine-tuningnlusequence-labeling

arXivPDFCodeMarkdown13 min read

AI-compiled by claude-haiku-5-5 on 2026-10-24, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.

In plain words

Fine-tuning a language model for a task means updating all of its weights. That needs a lot of memory, and each task needs its own full copy of the model. Prompt tuning freezes the model and learns only a few "soft" vectors that are fed in next to the text. It is cheap, but earlier work found it only worked well on very large models and on simple classification tasks.

P-tuning v2 also keeps the model frozen, but gives every layer its own learned vectors. Each layer reads them as extra context, so the vectors can steer what the model computes at every depth. A small classification head on top produces the answer. Only the vectors and the head are trained, so each task stores a small file instead of a full model.

The authors report that this works across model sizes and across tasks, including tagging words in a sentence and finding answer spans in a text. The gains are their own measurements on their own benchmarks.

TL;DR

  • P-tuning v2 is deep prompt tuning adapted to language understanding: trainable prefix vectors at every layer of a frozen backbone, plus a trainable head §3.2 §3.3.
  • Author-reported: comparable to full fine-tuning on SuperGLUE dev, SQuAD, and NER/SRL/CoNLL tasks, from BERT-large to GLM-xxlarge (10B), with 0.1%-3% trainable parameters §1 tab. 2 tab. 3.
  • The paper body fixes almost no hyperparameters. The released run scripts do, and some disagree with a commented-out appendix table in the LaTeX source.
  • SuperGLUE and SQuAD numbers are dev-set results, and the released code reports the best epoch on dev. The split for the NER and SRL numbers is not stated. Read the gaps with that in mind.

Problem

  • Fine-tuning must store gradients and optimizer state for every parameter, and keeping one model copy per task is inconvenient at inference §1.
  • Prompt tuning, which trains only continuous inputs, matches fine-tuning mainly on models of about 10B parameters and on simple classification tasks §1 §2. On 100M-1B models it performs much worse §3.1.
  • It also performs poorly on hard sequence labeling, such as extractive QA and NER §1 §3.1 §4.2.
  • Scope of the experiments: fully supervised, not few-shot, and every method except fine-tuning keeps the backbone frozen §4.

Method

Input-layer prompt tuning, as in Lester et al. and P-tuning, prepends trainable embeddings to the input embeddings only. The trainable part sits at the input §2 fig. 2. P-tuning v2 instead prepends trainable prefix vectors at every transformer layer, as keys and values, following deep prompt tuning §3.2 fig. 2.

  1. Freeze the whole pretrained backbone. Only the prompt table and the head are trained §4. The released code sets requires_grad = False on the backbone sequence_classification.py.
  2. Create a prompt table with one row per prefix position. Each row holds a key and a value vector for each layer §3.2 prefix_encoder.py. The code applies dropout to the prompt values sequence_classification.py.
  3. Optional reparameterization: a two-layer MLP (hidden size 512 by default) can encode the prompt rows. The paper says its usefulness depends on the task §3.3. The code uses a direct embedding table unless --prefix_projection is set arguments.py.
  4. Classification (sequence level): a randomly initialised linear head on the first-token representation, as in BERT §3.3. In the code, that representation is the backbone's pooler output sequence_classification.py.
  5. Sequence labeling: a linear classifier per token app. A. The NER code labels only the first sub-token of each word dataset.py.
  6. Extractive QA: a start and an end label per token, pick the most confident span, and call the question unanswerable below a threshold app. A. The code uses a fixed 384-token window with stride 128 dataset.py dataset.py.
  7. Multi-task (optional): several datasets of one task family share the prompt table, each with its own linear classifier §3.3 §4.

Prompt length matters per task: simple classification tasks usually prefer fewer than 20 prefix positions, and hard sequence labeling tasks prefer around 100 §3.3 fig. 4. In the RoBERTa-large ablation, the best length and the choice of reparameterization differ by dataset app. B fig. 4.

Prompt depth matters too. With the same number of prompted layers, adding prompts to the deeper layers beat adding them to the shallow ones on RTE and BoolQ with BERT-large §4.3 fig. 3.

Pseudocode
# P-tuning v2 forward pass, sequence classification
# Input:  input_ids (B, T), attention_mask (B, T)
# Output: logits (B, num_labels)
# The paper has no algorithm box; this is read from the released code.

def forward(input_ids, attention_mask):
    # One trainable row per prefix position  [§3.2]
    prefix_rows = prompt_table(arange(L))  # (L, 2 * layers * d)
    # Dropout on the prompt values
    # [code: model/sequence_classification.py#L367]
    prefix_rows = dropout(prefix_rows)
    # One key prefix and one value prefix per layer  [§3.2]
    past = split_per_layer(prefix_rows, batch_size=B)
    # Prefix positions are always attended (mask = 1)
    # [code: model/sequence_classification.py#L389]
    attention_mask = concat(ones(B, L), attention_mask)
    # Frozen backbone reads the prefix as past keys and values  [§3.2]
    outputs = frozen_backbone(input_ids, attention_mask, past)
    # Pooled first token from the frozen pooler  [§3.3]
    # [code: model/sequence_classification.py#L404]
    pooled = outputs.pooled
    # Trainable linear head after dropout  [§3.3]
    # [code: model/sequence_classification.py#L406-L407]
    logits = classifier(dropout(pooled))
    return logits

Equations: the paper has no numbered equations. Its input-sequence formulas in §2 are unnumbered and cite as §2. The per-layer scheme is described in words §3.2.

Hyperparameters

The paper body gives only the prompt length guidance above §3.3 and the 0.1%-3% parameter ratio §1. The table below is taken from the released run scripts. Every script sets these values on lines 5-9 run_rte_roberta.sh. Each row cites its own script. Unless noted, the common settings are: --prefix on, --seed 11 (checked in the RTE and SQuAD scripts), --hidden_dropout_prob as "Dropout", max_seq_length 128 by default arguments.py, and per-epoch evaluation.

TaskBackboneBatchLRDropoutPrefix lenEpochsSource
BoolQRoBERTa-large327e-30.18100run_boolq_roberta.sh
COPARoBERTa-large169e-30.18120run_copa_roberta.sh
RTERoBERTa-large325e-30.1128100run_rte_roberta.sh
WiCRoBERTa-large321e-20.1850run_wic_roberta.sh
WSCRoBERTa-large161e-20.1810run_wsc_roberta.sh
CoNLL03RoBERTa-large163e-20.11130run_conll03_roberta.sh (max_seq_length 152)
CoNLL04RoBERTa-large326e-20.114480run_conll04_roberta.sh
OntoNotes 5.0RoBERTa-large167e-30.14860run_ontonotes_roberta.sh
CoNLL05RoBERTa-large166e-30.122415run_conll05_roberta.sh (max_seq_length 128)
CoNLL12RoBERTa-large325e-30.16445run_conll12_roberta.sh
SQuAD 1.1RoBERTa-large85e-30.21630run_squad_roberta.sh
SQuAD 2.0RoBERTa-large85e-30.2810run_squad_v2_roberta.sh
BoolQBERT-large325e-30.140100run_boolq_bert.sh
COPABERT-large161e-20.11680run_copa_bert.sh
RTEBERT-large321e-20.12060run_rte_bert.sh
WiCBERT-large161e-40.12080run_wic_bert.sh with --prefix_projection (MLP)
WSCBERT-large165e-30.12080run_wsc_bert.sh
CoNLL04BERT-large322e-20.212840run_conll04_bert.sh
OntoNotes 5.0BERT-large161e-20.11630run_ontonotes_bert.sh
CoNLL12BERT-large165e-30.112845run_conll12_bert.sh

Other values in the code, not in the paper body:

NameValueWhereNotes
prefix MLP hidden size512arguments.pyused only with --prefix_projection
reparameterizationoffarguments.pydirect embedding table
QA n-best / max answer length20 / 30arguments.py
QA null-score threshold0.0arguments.pyno script overrides it
QA window / stride384 / 128dataset.py dataset.pyfixed in code, not a flag
NER/SRL/QA LR scheduleExponentialLR, gamma 0.95, once per epochtrainer_exp.py trainer_exp.pynot stated in the paper
SuperGLUE LR scheduleTrainer defaultsget_trainer.pynot stated in the paper
optimizer, weight decay, warmupnot statednot set by any script read

Results

Author-reported, on the dev sets of each benchmark tab. 2 tab. 3. Setup: frozen backbones for PT and PT-2, full fine-tuning for FT, fully supervised §4. PT is the input-layer prompt tuning baseline (Lester et al. and P-tuning). MPT-2 is multi-task P-tuning v2 tab. 3. Sizes: BERT-large 335M, RoBERTa-large 355M, DeBERTa-xlarge 750M, GLM-xlarge 2B, GLM-xxlarge 10B tab. 2 tab. 3.

SuperGLUE dev, FT / PT / PT-2 tab. 2. The metric for each row is not named in the caption, except MultiRC F1a and ReCoRD F1 as printed in the column headers:

DatasetBERT-largeRoBERTa-largeGLM-xlarge (2B)GLM-xxlarge (10B)
BoolQ77.7 / 67.2 / 75.886.9 / 62.3 / 84.888.3 / 79.7 / 87.088.7 / 88.8 / 88.8
CB94.6 / 80.4 / 94.698.2 / 71.4 / 10096.4 / 76.4 / 96.498.7 / 98.2 / 96.4
COPA69.0 / 55.0 / 73.094.0 / 63.0 / 93.093.0 / 92.0 / 91.098.0 / 98.0 / 98.0
MultiRC (F1a)70.5 / 59.6 / 70.685.7 / 59.9 / 82.584.1 / 77.5 / 84.488.1 / 86.1 / 88.1
ReCoRD (F1)70.6 / 44.2 / 72.889.0 / 46.3 / 89.391.8 / 82.7 / 91.994.4 / 87.8 / 92.5
RTE70.4 / 53.5 / 78.386.6 / 58.8 / 89.590.3 / 85.6 / 90.393.1 / 89.9 / 93.1
WiC74.9 / 63.0 / 75.175.6 / 56.9 / 73.474.1 / 71.0 / 72.075.7 / 71.8 / 74.0
WSC68.3 / 64.4 / 68.363.5 / 64.4 / 63.595.2 / 87.5 / 92.395.2 / 94.2 / 93.3

Sequence labeling and QA, FT / PT / PT-2 / MPT-2 tab. 3. NER and SRL are micro-F1. The caption does not say which split the sequence-labeling rows use, only that the SQuAD rows are dev.

DatasetBERT-largeRoBERTa-largeDeBERTa-xlarge (750M)
CoNLL03 (F1)92.8 / 81.9 / 90.2 / 91.092.6 / 86.1 / 92.8 / 92.893.1 / 90.2 / 93.1 / 93.1
OntoNotes 5.0 (F1)89.2 / 74.6 / 86.4 / 86.389.8 / 80.8 / 89.8 / 89.890.4 / 85.1 / 90.4 / 90.5
CoNLL04 (F1)85.6 / 73.6 / 84.5 / 86.688.8 / 76.2 / 88.4 / 90.689.1 / 82.4 / 86.5 / 90.1
CoNLL12 (F1)84.9 / 64.5 / 83.2 / 85.186.5 / 67.2 / 84.6 / 86.286.5 / 74.1 / 85.7 / 87.1
CoNLL05 WSJ (F1)88.5 / 76.0 / 86.3 / 88.590.2 / 76.8 / 89.2 / 90.091.2 / 82.3 / 90.6 / 91.2
CoNLL05 Brown (F1)82.7 / 70.0 / 80.7 / 83.185.6 / 70.7 / 84.3 / 85.786.9 / 77.7 / 86.3 / 87.0
SQuAD 1.1 dev (EM / F1)84.2 / 91.1 · 1.0 / 8.5 · 77.8 / 86.0 · 82.3 / 89.688.9 / 94.6 · 1.2 / 12.0 · 88.5 / 94.4 · 88.0 / 94.190.1 / 95.5 · 2.4 / 19.0 · 90.4 / 95.7 · 89.6 / 95.4
SQuAD 2.0 dev (EM / F1)78.7 / 81.9 · 50.2 / 50.2 · 69.7 / 73.5 · 72.7 / 75.986.5 / 89.4 · 50.2 / 50.2 · 82.1 / 85.5 · 83.4 / 86.788.3 / 91.1 · 50.2 / 50.2 · 88.4 / 91.1 · 88.1 / 90.8

In the SQuAD rows, each cell lists FT, PT, PT-2 and MPT-2 in turn, separated by " · ". The CoNLL12 PT value for BERT-large is typeset as "64.5." in the source. It is read here as 64.5.

Ablation, RoBERTa-large, accuracy (metric not named in the caption) tab. 4:

HeadSST-2RTEBoolQCB
[CLS] with linear head96.388.484.896.4
verbalizer with LM head95.886.684.694.6

Headline reading, with the paper's own framing: on RTE, PT-2 beats full fine-tuning for BERT-large (78.3 vs 70.4) and RoBERTa-large (89.5 vs 86.6) tab. 2. On SQuAD 2.0 with DeBERTa-xlarge, PT-2 matches fine-tuning (88.4 / 91.1 vs 88.3 / 91.1) tab. 3. The paper says PT-2 is comparable to fine-tuning at every scale it tested §4.1. The table does not support "always": for example, WiC with RoBERTa-large is 73.4 vs 75.6, and WSC with GLM-xxlarge is 93.3 vs 95.2 tab. 2.

Figures, in words only. fig. 1 shows the average dev score on RTE, BoolQ and CB for PT-2 against fine-tuning and prompt tuning across model scales. fig. 2 compares input-layer prompting with P-tuning v2: orange blocks are trainable prompts and blue blocks are embeddings from the frozen model. fig. 3 shows, for BERT-large on RTE and BoolQ, that adding the same number of prompts to deeper layers beats adding them to shallower layers. fig. 4 shows the prompt-length and reparameterization curves for RoBERTa-large on RTE, CoNLL04, BoolQ and CoNLL12; the paper says the best choice depends on the dataset app. B.

Paper vs code

  • Reparameterization default. The paper says the MLP helps on some datasets and not others §3.3. The code's default is off arguments.py. Only the BERT-large WiC script turns it on run_wic_bert.sh.
  • Hyperparameters in the LaTeX source but not in the PDF. appendix.tex holds a full per-task table inside \hide{...}, which the macro discards [raw/src/2110.07602/appendix.tex]. Some entries disagree with the scripts:
    • CoNLL03 RoBERTa-large: prefix length 112 in the hidden table; psl=11 in the script run_conll03_roberta.sh.
    • OntoNotes 5.0 RoBERTa-large: learning rate 7e-2 and 30 epochs in the hidden table; lr=7e-3 and epoch=60 in the script run_ontonotes_roberta.sh run_ontonotes_roberta.sh.
    • SQuAD 1.1 RoBERTa-large: batch size 16 in the hidden table; bs=8 in the script run_squad_roberta.sh.
    • WSC RoBERTa-large: prefix length 20 and 100 epochs in the hidden table; psl=8 and epoch=10 in the script run_wsc_roberta.sh.
    • Which of each pair produced the paper's numbers is not stated.
  • Best-epoch dev reporting. The base trainer keeps the best dev score over epochs and records its epoch trainer_base.py trainer_base.py. The released README lists a "Best Epoch" row beside its reproduced numbers README.md README.md. The paper does not say how the reported dev number was chosen.
  • Released numbers differ from the paper. The README's RoBERTa-large row gives RTE 86.6 and COPA 92.0 README.md. Table 2 gives PT-2 RTE 89.5 and COPA 93.0 tab. 2. The README's BERT-large row gives CoNLL12 85.3 and COPA 77.0 README.md. Table 3 gives 83.2 for CoNLL12 and table 2 gives 73.0 for COPA. The README says reproduction may need tuning for the local environment README.md.
  • Head. The paper says a linear head on the [CLS] position §3.3. The code reads the backbone's pooler output, which is a dense layer with tanh over the first token, and that pooler is frozen sequence_classification.py sequence_classification.py.
  • Multi-task. The paper describes MPT-2 §3.3 §4 tab. 3. No multi-task training code or script was found in the repository. Every run script trains one dataset.
  • Models without configs. The repository has no run script for DeBERTa, GLM, CB, MultiRC, ReCoRD, or BERT-large on SQuAD and CoNLL03. GLM appears only in the README. The DeBERTa classes exist in the code utils.py, but no script calls them.
  • Prompt-tuning baseline. The --prompt path covers only sequence classification and multiple choice utils.py. Token classification and QA have no baseline path, so the PT rows of table 3 cannot be reproduced from this code. No --prompt script exists.
  • Sub-token labels. The paper says nothing about sub-tokens. The NER code labels only the first sub-token of each word and sets the rest to -100 dataset.py. A comment in the code mentions a label_all_tokens flag, but the flag is not used.
  • SRL verb. The paper says the target verb token is appended to the sentence app. A. The code appends it with no word id, so the loss ignores it dataset.py.
  • Unanswerable questions. The paper gives a threshold but no value app. A. The code defaults the null-score threshold to 0.0 and no script overrides it arguments.py.

Pitfalls

  • Dev set, best epoch. The SuperGLUE and SQuAD numbers are dev numbers, and the code keeps the best epoch on dev. A test score from the same recipe would likely be lower. The paper does not say how many runs or seeds each cell averages. The scripts I read fix seed 11.
  • Fully supervised only. The paper's own scope excludes few-shot settings §4. The README also says its SuperGLUE comparison with P-tuning v1 uses a different backbone regime, with the backbone tuned jointly README.md.
  • Frozen is not eval mode. The code sets requires_grad = False on the backbone sequence_classification.py. HF Trainer normally switches the whole model to training mode, which would keep the backbone's dropout active during training. This is standard Trainer behaviour; I did not check it in this repository.
  • Frozen pooler. The pooler belongs to the frozen backbone, so the classifier sits on a fixed nonlinear projection of the first token sequence_classification.py.
  • Long prefixes are not free. The prefix is concatenated to the keys and values of every layer, so each prefix position adds attention work at every layer. CoNLL05 RoBERTa-large uses 224 prefix positions run_conll05_roberta.sh. The paper does not discuss this cost.
  • Learning-rate sensitivity. Learning rates in the scripts range from 1e-4 to 6e-2 across tasks run_wic_bert.sh run_conll04_roberta.sh. A single rate does not transfer. The README says the best hyperparameters can be sensitive to the environment and recommends a search README.md.
  • Optimizer details are missing. The optimizer, weight decay, and warmup are not stated in the paper, and no script sets them. Only the NER/SRL/QA exponential schedule is explicit in the code.
  • Parameter ratio. The 0.1%-3% figure is computed by the paper against transformer parameters §4. The code prints a trainable count, but I did not reproduce the ratio.
  • Typo in the source table. CoNLL12 PT for BERT-large is typeset "64.5." tab. 3 source.

Lineage

  • Builds on: deep prompt tuning, as in prefix-tuning (Li and Liang, arXiv:2101.00190) §1 §3.2. The paper also cites Qin and Eisner's soft prompts tab. 1.
  • Compared against: Lester et al., "The power of scale for parameter-efficient prompt tuning", arXiv:2104.08691 §1 tab. 2. P-tuning (GPT Understands, Too) §1. Full fine-tuning tab. 2.
  • Superseded by: not stated in the paper, and no card in this wiki covers a successor.

Source and license

Adapted from “P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks” by Xiao Liu, Kaixuan Ji, Yicheng Fu, Weng Lam Tam, Zhengxiao Du, Zhilin Yang, Jie Tang (arXiv:2110.07602), 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/THUDM/P-tuning-v2 at commit b1520c9aa177 (Apache-2.0). Any quoted code is shown under that license.