
使用 sentence-transformers 加载 DPR 模型Dense Passage Retrieval 双编码器在开放域问答中的实战指南【免费下载链接】sentence-transformersState-of-the-Art Embeddings, Retrieval, and Reranking项目地址: https://gitcode.com/gh_mirrors/se/sentence-transformers本文面向需要在开放域问答Open-Domain QA场景中构建「段落检索 答案定位」管线的开发者介绍如何在 sentence-transformers 中加载基于 Dense Passage RetrievalDPR 论文训练的预训练模型分别编码「问题」与「段落」并通过点积dot-product完成相关度排序。读完本文你将掌握 DPR 双编码器模型对ctx_encoder/question_encoder的加载方式、段落输入必须包含「标题 正文」并以[SEP]分隔的格式要求以及为什么这类模型必须搭配点积而不是余弦相似度使用。DPR 模型背景与模型系列Dense Passage Retrieval密集段落检索由 Karpukhin 等人提出其核心思想是训练两个独立的编码器一个用于编码问题/查询一个用于编码文档/段落将二者映射到同一向量空间使得相关的问题-段落对在该空间中距离更近。sentence-transformers 将这两类官方 DPR 权重直接封装为可即插即用的SentenceTransformer模型共包含四个模型见 docs/pretrained-models/dpr.md数据集段落编码器ctx_encoder问题编码器question_encoderGoogle Natural Questions单一数据集sentence-transformers/facebook-dpr-ctx_encoder-single-nq-basesentence-transformers/facebook-dpr-question_encoder-single-nq-baseNatural Questions TriviaQA WebQuestions CuratedTREC多数据集组合sentence-transformers/facebook-dpr-ctx_encoder-multiset-basesentence-transformers/facebook-dpr-question_encoder-multiset-base其中single-nq 系列仅在 Google Natural Questions 数据集上训练该数据集包含约 10 万条真实 Google 搜索查询及其对应的维基百科相关段落对「问题-答案」式检索任务最为专注multiset 系列在 Natural Questions、TriviaQA、WebQuestions 与 CuratedTREC 四个数据集的组合上训练覆盖的查询风格更广鲁棒性更强。值得说明的是每一对模型中始终包含两个独立编码器一个负责编码段落ctx_encoder一个负责编码问题/查询question_encoder二者不能混用。在使用时请务必为查询与段落分别加载对应的模型。双编码器使用模式段落编码DPR 的段落编码有一个与其他SentenceTransformer模型明显不同的输入约定编码段落时必须同时提供维基百科条目标题与正文文本二者之间用[SEP]特殊 token 分隔。原因在于 DPR 模型在训练时就是按「标题[SEP]正文」的拼接格式喂入上下文编码器的因此推理时必须复刻相同的格式才能获得有意义的向量。from sentence_transformers import SentenceTransformer # 加载段落编码器ctx_encoder passage_encoder SentenceTransformer(sentence-transformers/facebook-dpr-ctx_encoder-single-nq-base) # 每个段落 标题 [SEP] 正文 passages [ London [SEP] London is the capital and largest city of England and the United Kingdom., Paris [SEP] Paris is the capital and most populous city of France., Berlin [SEP] Berlin is the capital and largest city of Germany by both area and population., ] passage_embeddings passage_encoder.encode(passages)可以看到每个字符串都以「维基百科文章标题」开头紧跟[SEP]后才是该标题对应段落的具体正文。若输入缺少标题或未使用[SEP]分隔编码结果将与模型训练分布不一致检索效果会显著下降。双编码器使用模式查询编码与点积打分查询则使用另一侧的question_encoder进行编码。编码完成后需要计算查询向量与所有段落向量的相似度分数并排序from sentence_transformers import SentenceTransformer from sentence_transformers.util import dot_score query_encoder SentenceTransformer(sentence-transformers/facebook-dpr-question_encoder-single-nq-base) query What is the capital of England? query_embedding query_encoder.encode(query) # 重要必须使用点积dot-product而不是余弦相似度cosine_similarity scores dot_score(query_embedding, passage_embeddings) print(Scores:, scores)输出为一个形状为(1, 3)的分数矩阵第j列即查询与第j个段落的点积分数。分数最高的段落即为检索到的「最相关段落」例如上例中应为London [SEP] London is the capital...。为什么必须使用点积而非余弦相似度这是使用 DPR 模型时最容易踩的坑也是官方文档中特别标注的重要注意事项DPR 模型必须搭配点积dot-product使用不能使用余弦相似度。从实现层面看sentence-transformers 提供的dot_score位于 sentence_transformers/util/similarity.pydef dot_score(a: list | np.ndarray | Tensor, b: list | np.ndarray | Tensor) - Tensor: ... a _convert_to_batch_tensor(a) b _convert_to_batch_tensor(b) return torch.mm(a, b.transpose(0, 1)).to_dense()而余弦相似度cos_sim在 同一文件 中会先对向量做 L2 归一化normalize_embeddings再计算内积def cos_sim(a, b): a _convert_to_batch_tensor(a) b _convert_to_batch_tensor(b) a_norm normalize_embeddings(a) b_norm normalize_embeddings(b) return torch.mm(a_norm, b_norm.transpose(0, 1)).to_dense()两者的差别在于余弦相似度先归一化再内积等价于「归一化后的点积」会丢失向量模长信息并改变分数分布而 DPR 模型在训练NLL 损失与评估阶段使用的都是未归一化的点积分数向量模长本身携带重要的排序信息。因此对 DPR 模型而言直接使用dot_score才能复现论文中的检索行为使用余弦相似度会导致排序退化。如果希望以更规范的方式选择相似度函数也可以使用仓库中的SimilarityFunction枚举同样定义于 sentence_transformers/util/similarity.pyfrom sentence_transformers.util import SimilarityFunction # DOT_PRODUCT 的别名包括 dot / dot_product similarity_fn SimilarityFunction.to_similarity_fn(SimilarityFunction.DOT_PRODUCT) scores similarity_fn(query_embedding, passage_embeddings)该枚举支持cosine、dot、euclidean、manhattan、maxsim、meanmaxsim等多种函数并可通过SimilarityFunction.to_similarity_fn统一分发方便在代码中集中管理相似度策略。仓库中的 DPR 架构支持情况sentence-transformers 的测试体系中同样覆盖了 DPR 架构dpr在 tests/base/modules/transformer/transformers_tiny_models.json 中dpr被映射到 transformers 官方的微型测试权重hf-internal-testing/tiny-random-dpr用于架构加载与转换的验证。不过需要注意的是在 tests/base/modules/transformer/conftest.py 中dpr被列入XFAIL_ARCHITECTURES列表其标注的原因为「Model doesnt output last_hidden_state on forward/expose get_..._features methods」。也就是说从测试视角看DPR 模型的前向输出接口与通用的feature-extraction约定存在差异官方对这类「开箱即用的通用文本嵌入」场景是标记为预期失败xfail的。这一点也从侧面印证了 DPR 模型的定位它们是面向「问题-段落」双塔检索的专用模型而非通用的句子嵌入模型使用时请遵循本文的双编码器 点积范式不要将其当作普通SentenceTransformer模型直接用于句子相似度任务。相关预训练模型与性能参考如果你的目标是直接做「基于维基百科段落的问答检索」还可以关注仓库中另一个相关文档 docs/pretrained-models/nq-v1.md。该文档介绍了同样在 Natural Questions 数据集上训练的sentence-transformers/nq-distilbert-base-v1模型并给出了在 Natural Questions 开发集NQ dev set small上以MRR10为指标的对照数据ApproachMRR10NQ dev set smallnq-distilbert-base-v172.36其他模型DPR58.96该对照表可以帮助你在选择「DPR 双塔模型」与「单模型问答检索模型」之间做出权衡。值得一提的是nq-distilbert-base-v1同样要求将段落编码为「标题 正文」的列表输入形式即[[title1, text1], ...]与 DPR 的输入约定一脉相承可一并参考。小结与最佳实践清单在 sentence-transformers 中使用 DPR 模型时请始终遵循以下要点成对加载段落用facebook-dpr-ctx_encoder-*-base查询用facebook-dpr-question_encoder-*-base不要混用段落格式每个段落必须是标题 [SEP] 正文的字符串格式标题不可省略相似度度量一律使用dot_score点积打分与排序严禁使用余弦相似度模型定位DPR 是「问题-段落」双塔检索专用模型适合作为开放域问答系统的召回阶段Retriever不适用于通用的句子相似度/聚类等场景数据集选择single-nq 系列专注于 Natural Questions 风格查询multiset 系列覆盖 TriviaQA、WebQuestions、CuratedTREC 等更多数据分布泛化面更广。掌握上述要点后你便可以将 DPR 双编码器无缝接入 sentence-transformers 生态快速搭建一套面向维基百科语料的开放域问答检索原型。【免费下载链接】sentence-transformersState-of-the-Art Embeddings, Retrieval, and Reranking项目地址: https://gitcode.com/gh_mirrors/se/sentence-transformers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考