wikillm

Paper · 2023-12-24 · cs.CL

Llama2Vec: Unsupervised Adaptation of Large Language Models for Dense Retrieval

Adapts LLaMA-2-7B for dense retrieval without labels. A joint prompt asks the model to rebuild a Wikipedia passage (EBAE) and to predict the next passage (EBAR), read from two special-token embeddings; a custom attention mask keeps the two prompts apart. After LoRA fine-tuning on MS MARCO it reports 43.1 MRR@10 on passages and 56.4 average NDCG@10 on zero-shot BEIR. Author-reported.

Dense retrievalText embeddingContrastive learningLow-rank adaptation (LoRA and variants)dense-retrievaltext-embeddingllm-embeddingsunsupervised-adaptationms-marcobeir

arXivPDFCodeMarkdown15 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

A search engine that finds documents by meaning first turns each query and each document into a list of numbers, then returns the documents whose lists look most like the query's. Large language models understand text well, but they were trained to guess the next word, so the numbers they produce for a whole text are not tuned for this comparison.

The authors teach the model, before any search training, to build a summary of a text that is good enough to rebuild the text and to guess the text that comes after it. The model does this by reading the passage followed by two short instructions, and it reads the answer from a small marker placed after each instruction. The extra training uses only ordinary Wikipedia text, with no labelled search data. After that, the usual search training is run on top, and the authors report better results than earlier models on the standard benchmarks.

TL;DR

  • Llama2Vec is an unsupervised step that adapts LLaMA-2-7B to produce one embedding per text for dense retrieval. It is trained on Wikipedia with two pretext tasks, EBAE (rebuild the input) and EBAR (predict the next text) §1 §3.2.
  • The two prompts share one forward pass. A modified attention mask keeps them from seeing each other, which the authors say saves almost half the compute of two separate passes §3.2 fig. 2.
  • Search fine-tuning uses contrastive learning with LoRA on MS MARCO §4.1.
  • Author-reported: MS MARCO passage MRR@10 43.1 (RepLLaMA 41.2) tab. 1; document MRR@100 47.9 (RepLLaMA 45.6) tab. 2; BEIR zero-shot average NDCG@10 56.4 (RepLLaMA 53.9) tab. 3.
  • The released code does not match the paper in several places: the prompts, the embedding read-out, an extra next-token loss, the loss form, and position ids. Read "Paper vs code" before implementing.

Problem

Dense retrieval represents queries and documents as vectors in one space and ranks documents by similarity §3.1. Earlier work reached good results with BERT-sized encoders §2. Decoder-only LLMs carry more knowledge, but they are trained to predict the next token, so their hidden state at the last position mainly describes what comes next, not the meaning of the whole text. The authors call this a mismatch between language modelling and text embedding §1 §2.

Method

Embedding read-out (baseline). A decoder-only model sees the whole input only at its last token, so a single text embedding is usually read from a special end-of-text token. For LLaMA the paper writes this as the state of that token after the input §3.1 eq. 1.

Joint prompt. Llama2Vec feeds the input text T followed by two prompt blocks: SELF, with the words "The input sentence is:", and NEXT, with the words "The next sentence is:". Each block ends in a special token. The embedding e^α is the state at the end of the SELF block, and e^β is the state at the end of the NEXT block §3.2 fig. 1 eq. 2 eq. 3.

Attention mask. The SELF and NEXT blocks cannot attend to each other, but both can attend to T. Because T is most of the sequence, the authors say one joint pass saves almost 50 percent of the cost of two passes §3.2 fig. 2.

Pretext objective. For each embedding, a linear projection to the vocabulary predicts the tokens of its target: the input itself for e^α (EBAE) and the next sentence for e^β (EBAR). The loss is the average negative log-likelihood of those target tokens §3.2 eq. 4. The paper does not say how the two terms are weighted.

Pseudocode
# Llama2Vec adaptation loss, as the paper describes it  [§3.2] [eq. 2-4]
# Input:  text ids T, the same text as summary target, next-text ids
# Output: scalar loss

def adaptation_loss(text_ids, next_ids, lm_head):
    # Joint prompt: T, then the SELF block, then the NEXT block  [§3.2]
    prompt = text_ids + SELF_ids + NEXT_ids
    # Blocks may not attend to each other  [§3.2] [fig. 2]
    hidden = llama(prompt, mask=joint_prompt_mask(prompt))  # (L, d)
    # Inductive embedding: state at the end of the SELF block  [eq. 2]
    e_alpha = hidden[end_of_self_block]  # (d,)
    # Deductive embedding: state at the end of the NEXT block  [eq. 3]
    e_beta = hidden[end_of_next_block]  # (d,)
    # Predict each target token from its embedding  [§3.2] [eq. 4]
    loss_alpha = mean(-log_softmax(lm_head(e_alpha))[text_ids])
    loss_beta = mean(-log_softmax(lm_head(e_beta))[next_ids])
    # Paper does not state how the two terms are combined
    return loss_alpha + loss_beta

Fine-tuning. The adapted model is trained with contrastive learning on (query, answer) pairs: the positive is scored against the other passages in the batch and against hard negatives §3.2 eq. 5 §4.1. Queries use the NEXT prompt and answers use the SELF prompt in the default QA setting. For paraphrase tasks the authors use SELF for long documents and NEXT for short texts §3.2. Fine-tuning uses LoRA on the LLM §4.1.

Pseudocode
# Contrastive fine-tuning step  [§3.2] [eq. 5] [§4.1]
# Input:  queries, positives, hard negatives; temperature
# Output: scalar loss

def finetune_loss(queries, positives, negatives, temperature):
    # Query embedding with the NEXT prompt  [§3.2]
    query_emb = normalize(embed(queries, block="NEXT"))  # (B, d)
    # Candidates (positives and negatives) with the SELF prompt  [§3.2]
    cand_emb = normalize(embed(candidates, block="SELF"))  # (B*(K+1), d)
    # Similarity of every query with every candidate in the batch
    # [code: research/LLARA/finetune/modeling.py#L107-L110]
    scores = query_emb @ cand_emb.T / temperature  # (B, B*(K+1))
    # Each query's positive sits at its own index; the rest are negatives
    # [code: research/LLARA/finetune/modeling.py#L128-L130]
    return cross_entropy(scores, positive_index)

Key equations, in the paper's notation:

\boldsymbol{e}_t \leftarrow \mathrm{LLaMA}(T)[\langle\backslash s\rangle] eq. 1

\boldsymbol{e}^{\alpha}_t \leftarrow \mathrm{LLaMA}(T,\mathrm{SELF},\langle\backslash s\rangle)[-1] eq. 2

\boldsymbol{e}^{\beta}_t \leftarrow \mathrm{LLaMA}(T,\mathrm{NEXT},\langle\backslash s\rangle)[-1] eq. 3

\min -\frac{1}{|\mathcal{T}|}\sum_{t\in\mathcal{T}} \log\frac{\exp(\boldsymbol{e}^{\top}\boldsymbol{W}_{t})} {\sum_{v\in V}\exp(\boldsymbol{e}^{\top}\boldsymbol{W}_{v})} eq. 4

\min \sum_{q} -\log\frac{\exp(\langle \boldsymbol{e}^{\alpha}_{q},\boldsymbol{e}^{\beta}_{a}\rangle)} {\sum_{a'\in\mathcal{A}'}\exp(\langle \boldsymbol{e}^{\alpha}_{q},\boldsymbol{e}^{\beta}_{a'}\rangle)} eq. 5

Eq. 5 is printed with the query in α and the answer in β, which contradicts the text that follows it (see Pitfalls).

Hyperparameters

Adaptation and fine-tuning settings. "not stated" means the paper gives no value.

NameValueWhereNotes
Base modelLLaMA-2-7B (base)§4.1
Adaptation corpusWikipedia, as curated by DPR§4.1Public dataset linked in the README README.md
Adaptation steps10,000§4.1
Adaptation batch size256§4.1README example uses 8 GPUs at batch 1 with one epoch on toy data README.md
Adaptation sequence length1024§4.1Code default 512 arguments.py; README example 128 README.md
Adaptation learning rate1e-5§4.1Same value in the README README.md
Adaptation tuning modenot stated§4.1Code default is LoRA, but the README turns it off, so full training arguments.py README.md
Adaptation optimizernot stated§4.1Hugging Face Trainer defaults, with a DeepSpeed ZeRO stage-1 config stage1.json
Adaptation warmupnot stated§4.1README uses 0.1 of steps README.md
Adaptation precisionnot stated§4.1README uses bf16 README.md
Fine-tuning tuning modeLoRA§4.1Defaults: rank 64, alpha 16, dropout 0.1, on q, k, v, o, gate, up and down projections arguments.py
Fine-tuning hard negatives"ANN hard negatives"§4.1Number per query not stated; code default 8 total, README 16 arguments.py README.md
Fine-tuning temperaturenot stated§3.2Code default 0.02 arguments.py; README command 0.01 README.md
Embedding normalisationL2, in fine-tuningarguments.pyNot stated in the paper
Fine-tuning learning ratenot stated§4.1README uses 3e-4 README.md
Fine-tuning epochsnot stated§4.1README uses 1 README.md
Fine-tuning max query lengthnot stated—Code default 32; README 64 arguments.py README.md
Fine-tuning max passage lengthnot stated—Code default 128; README 160 README.md
Embedding dimension4096§4.4 tab. 7Full size before reduction; smaller sizes in tab. 7
Summary prompt (SELF)"The input sentence is:"§3.2Code uses a different wording, see Paper vs code
Next-text prompt (NEXT)"The next sentence is:"§3.2Code uses a different wording, see Paper vs code
Sparse embedding size Nnot stated§4.4Top-N entries kept
BM25 settings in lexical studynot stated§4.4Parameters k1 and b not given

Results

Author-reported. Setup: LLaMA-2-7B adapted on Wikipedia, then fine-tuned on MS MARCO passage or document data; evaluated on MS MARCO dev and DL'19 and DL'20 for retrieval, and on BEIR (zero-shot) and a LlamaIndex set §4.2 §4.3. The paper reports no variance or seed information.

Passage retrieval on MS MARCO tab. 1. Columns: dev MRR@10, dev Recall@1000, DL'19 NDCG@10, DL'20 NDCG@10.

MethodSizeFT.Dev M@10Dev R@1000DL'19 N@10DL'20 N@10
BM25----18.485.350.648.0
ANCE125Mhard33.095.964.8--
ADORE110Mhard34.7--68.3--
Condenser110Mhard36.697.469.8--
coCondenser110Mhard38.298.471.768.4
TAS-B55Mdistill34.397.672.269.2
RocketQAv2--distill38.898.1----
AR2+SimANS110Mdistill40.998.7----
GTR-XXL4.8B--38.899.0----
SimLM110Mhard39.198.669.869.2
SimLM+distill110Mdistill41.198.771.469.7
RetroMAE110Mhard39.398.5----
RetroMAE+distill110Mdistill41.698.868.1--
RetroMAEv2+distill110Mdistill42.698.975.1--
LLaMA2-RepLLaMA7Bhard41.299.474.372.1
OpenAI-Ada-002----34.498.670.467.6
LLaMA2-Llama2Vec7Bhard43.199.573.472.9

Document retrieval on MS MARCO tab. 2. Columns: dev MRR@100, dev Recall@100, DL'19 NDCG@10, DL'20 NDCG@10.

MethodSizeFT.Dev MRR@100Dev R@100DL'19 N@10DL'20 N@10
BM25----27.780.951.952.9
PROP110M--39.488.459.6--
B-PROP110M--39.588.360.1--
COIL110Mhard39.7--63.6--
ANCE (first-p)125Mhard37.789.361.5--
ANCE (max-p)125Mhard38.490.662.8--
ADORE110Mhard40.591.962.8--
COSTA110Mhard42.291.962.6--
LLaMA2-RepLLaMA7Bhard45.6--65.063.2
LLaMA2-Llama2Vec7Bhard47.994.168.263.6

Zero-shot retrieval on BEIR, NDCG@10 tab. 3. Columns are the models; the paper gives no per-model size for BM25 or Ada-2.

DatasetBM25BERTGTR-XXLCPT-XLAda-2SGPTRepLLaMALlama2Vec
Size--110M4.8B175B--5.8B7B7B
T-COVID59.561.550.164.981.387.384.786.9
NFCorpus32.226.034.240.735.836.237.838.2
NQ30.646.756.8--48.252.462.464.6
HotpotQA63.348.859.968.865.459.368.570.1
FiQA23.625.246.751.241.137.245.848.5
ArguAna39.726.554.043.556.751.448.656.5
Touche44.225.925.629.128.025.430.534.2
Quora78.978.789.263.887.684.686.888.3
DBPedia31.831.440.843.240.239.943.745.9
SCIDOCS14.111.316.1--18.619.718.118.9
FEVER65.168.274.077.577.378.383.481.3
C-FEVER16.518.726.722.323.730.531.038.2
SciFact67.953.366.275.473.674.775.674.8
CQA32.528.239.9--41.738.137.443.2
Average42.939.348.6--51.452.153.956.4

I recomputed the Llama2Vec column: the 14 values average 56.4, which matches the printed average. Llama2Vec beats BM25 on 13 of 14 datasets (not on Touche) and RepLLaMA on 12 of 14 (not on FEVER or SciFact).

Llama Index zero-shot retrieval tab. 4. The dataset is not described in the paper; the metric names are MRR and Hit Rate.

MethodMRRHit Rate
bge-m369.6788.94
OpenAI-TE3-S65.6989.42
OpenAI-TE3-L67.3790.38
RepLLaMA68.4391.35
Llama2Vec70.5692.31

Prompt usage on BEIR, NDCG@10 tab. 5. N2S = NEXT for the query and SELF for the document, S2S = SELF for both, N2N = NEXT for both, None = no prompt, Ada* = the per-dataset choice the paper argues for.

PromptAvgARCFDBFVFQHQNFNQQRSDSFTOTCCQ
N2S56.150.527.745.983.548.570.138.264.683.718.976.534.286.941.2
S2S30.256.520.610.433.116.727.418.38.888.07.167.83.134.226.0
N2N52.349.938.240.581.343.865.535.354.988.320.574.822.663.943.2
None47.653.826.740.072.338.963.832.346.188.317.774.315.049.641.1
Ada*57.456.538.245.981.348.570.138.264.688.318.974.834.286.943.2

The Ada* values per dataset are the same as the Llama2Vec column of tab. 3. The printed Ada* average (57.4) does not match its own 14 values, which average 56.4. I also get 55.0 for the N2S row, against 56.1 printed.

Lexical similarity of top-N vocabulary predictions, measured with BM25 tab. 6. Units are not stated; the paper says larger means more lexical overlap.

Top-NInitialAdaptFine-Tune
101.852.7413.83
10018.0147.6884.08
50093.68205.42307.30
1000219.09392.80542.89

Embedding dimension tab. 7. The metric is not named in the caption. DimRed means a jointly learned projection during fine-tuning; DimRed* means the projection is learned by distillation on a frozen retriever §4.4; Sparse keeps the top-N entries §4.4.

Dim.DimRedDimRed*Sparse
76841.041.241.5
102441.041.341.9
204841.141.442.3
409643.143.143.1

The technical-factor studies are the prompt study on BEIR tab. 5, the lexical study on MS MARCO tab. 6 and the dimension study tab. 7. The lexical study supports the claim that adaptation raises BM25 similarity between the query's and the answer's predicted vocabulary §4.4.

Paper vs code

Official repository: https://github.com/FlagOpen/FlagEmbedding at commit fd1a2bdf69488ffebe0327999d4400d8c8058a0b, MIT licence, copyright held by Shitao Xiao, an author. The paper's abstract links it [abstract]. The method is under research/LLARA/ (the paper's earlier name is LLARA). The repository contains the adaptation code (pretrain/), the fine-tuning code (finetune/), a toy dataset and a README, but not the MS MARCO or BEIR evaluation scripts and not the Wikipedia data, which the README links on Hugging Face README.md.

Differences between paper and code:

  • Prompt text. The paper gives SELF as "The input sentence is:" and NEXT as "The next sentence is:" §3.2. The code wraps the input in quotes and adds a suffix that asks to summarise or predict the passage in eight words: "summarize the above passage within eight words" and "predict the following passage within eight words" data.py data.py.
  • Read-out tokens. The paper uses one special token per block, with the state at that token as the embedding §3.2 eq. 2 eq. 3. The code adds sixteen new tokens <s1> to <s16> and resizes the embedding matrix; each block uses eight of them run.py modeling.py.
  • Embedding at inference and in fine-tuning. The paper reads the single token's state eq. 2 eq. 3. The code takes the mean of the last eight hidden states, for queries and passages alike modeling.py README.md. The README's usage example, which loads the released checkpoints, takes this mean.
  • Adaptation loss. The paper has only the multi-class term of eq. 4. The code adds next-token cross-entropy on the input passage tokens modeling.py, which the paper does not mention.
  • Bag-of-words term. The code does not use a per-token average over the target sentence. It max-pools the logits over the eight positions of each block, applies log-softmax, averages the negative log-probability over the unique target ids above 2 (which drops padding and special tokens), and divides by 10 modeling.py modeling.py. The two terms are then averaged modeling.py.
  • Attention mask. The code blocks the predict block from the summary block columns and leaves the rest of the causal mask as is modeling.py. Under a causal mask, this is the same as the paper's "mutually invisible" rule.
  • Position ids. The code sets the predict block's position ids to those of the summary block modeling.py. The paper does not describe position ids.
  • Attention implementation. The adaptation code refuses FlashAttention-2 modeling.py, so the README disables it README.md.
  • Stop words. The code has an option to remove stop words and punctuation from the bag-of-words targets. It is off by default arguments.py data.py, and the README turns it on README.md. The paper does not mention it.
  • Adaptation data. The paper says "next sentence" §3.2. The toy data has whole passages: the input, the same input as the summary target, and a following passage toy_pretrain_data.jsonl. How the real corpus was split is not stated.
  • Fine-tuning temperature. Not in the paper. Code default 0.02 arguments.py; README 0.01 README.md.
  • Hard negatives. The paper uses ANN hard negatives §4.1. The code reads a neg field from each training record and samples from it data.py. Where those negatives come from is not in the repository. The README says the released BEIR model uses BM25 negatives and the passage model uses dense-retriever negatives README.md README.md.
  • Batch size and steps. The paper uses batch 256 for 10,000 steps §4.1. The README example uses batch 1 per GPU on 8 GPUs for one epoch on toy data README.md, and says to use the paper's hyperparameters for the full data.
  • Title. The README cites an earlier title, "Making Large Language Models A Better Foundation For Dense Retrieval" README.md.

No discrepancies were found for the learning rate: 1e-5 in both §4.1 and README.md.

Pitfalls

  • Read-out choice. The paper's embedding is one special-token state. The code's embedding is the mean of eight special-token states, and the README's usage example, which loads the released models, takes that mean. Implement the code's read-out if you want the reported numbers to be reachable, and state that you did so.
  • Eq. 5 notation. eq. 5 puts the query embedding in α (SELF) and the answer embedding in β (NEXT). The text right after says the query uses NEXT and the answer uses SELF §3.2, and the code does the same data.py README.md. Follow the text.
  • Eq. 2 and eq. 3 use index −1. That is the last position of the separate prompt. In the joint prompt, the paper reads the SELF and NEXT states from their own blocks, not from the last position. The index notation does not describe the joint form.
  • Joint prompt only during adaptation. The custom mask exists only in the adaptation code. Fine-tuning and inference encode each block as its own prompt with the standard model modeling.py README.md. An implementation that uses the joint mask at inference would differ from the released model.
  • Multi-term loss. The paper does not say how the two pretext losses are combined. The code uses one next-token loss plus two bag-of-words terms with a factor of 1/10 modeling.py.
  • Max-pooling in the bag-of-words term. The code takes the max over eight positions before the softmax. This is not the per-token formula of eq. 4, and it is not in the paper.
  • Adaptation is full training in the README, not LoRA. The paper does not say which. The released command sets use_lora False README.md, while the code default is LoRA arguments.py.
  • Sequence length. The paper uses 1024 tokens §4.1. The code default is 512 and the README uses 128 arguments.py README.md.
  • Stop-word removal is not in the paper. The README enables it. Whether the reported results used it is not stated.
  • Optimizer, warmup and precision are not stated in the paper. Code uses the Hugging Face Trainer defaults with a DeepSpeed stage-1 config, a warmup of 0.1, and bf16 in the README stage1.json README.md README.md.
  • Which MS MARCO model feeds BEIR. The paper evaluates a model fine-tuned on MS MARCO passages §4.3, but it does not say which negative sampler it used. The released passage and BEIR models differ in negatives README.md README.md.
  • Table 5 average. The printed Ada* average (57.4) and N2S average (56.1) do not match their rows (56.4 and 55.0 by my recount). Use the per-dataset values; do not rely on the printed averages.
  • Table 7 metric and sparse size. The metric is not named, and N for the sparse variant is not stated §4.4.
  • Llama Index set. The dataset, its size and the metric definitions are not described tab. 4. The comparison models may use different settings.
  • Lexical study. The BM25 parameters and the units of tab. 6 are not stated.
  • Cost claim. The 50 percent saving is the authors' statement from the shared-prompt design §3.2. No timing measurement is reported.
  • Single runs. The paper reports one number per cell with no seeds or significance tests tab. 1 tab. 2 tab. 3. Differences of a point or two on small BEIR sets may not hold up.
  • Ethical limits. The authors do not recommend the model for sensitive retrieval, because LLaMA-2 carries its biases into the embeddings §7.

Lineage

  • Builds on: LLaMA-2-7B (Touvron et al. 2023) as the backbone §4.1; the fine-tuning recipe of RepLLaMA (Ma et al. 2023), which this paper follows and uses as its closest baseline §4.1 §4.2; LoRA (Hu et al. 2021) §4.1; ANN hard negatives (Xiong et al. 2020) §4.1; the Wikipedia corpus curated by DPR (Karpukhin et al. 2020) §4.1. None of these has a card in this wiki yet.
  • Compared against: BM25, ANCE, ADORE, Condenser, coCondenser, TAS-B, RocketQAv2, AR2+SimANS, GTR-XXL, SimLM, RetroMAE, RetroMAEv2, SGPT, OpenAI-Ada-002, bge-m3 and OpenAI text-embedding-3 tab. 1 tab. 2 tab. 3 tab. 4. The strongest baseline is RepLLaMA §4.2.
  • Superseded by: not stated in the wiki.

Source and license

Adapted from “Llama2Vec: Unsupervised Adaptation of Large Language Models for Dense Retrieval” by Zheng Liu, Chaofan Li, Shitao Xiao, Yingxia Shao, Defu Lian (arXiv:2312.15503), 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/FlagOpen/FlagEmbedding at commit fd1a2bdf6948 (MIT). Any quoted code is shown under that license.