许可证:Apache-2.0
语言:
数据集:
标签:
- SPLADE++
- 文档扩展
- 稀疏表示
- 词袋模型
- 段落检索
- 知识蒸馏
- 文档编码器
简称:针对工业场景进行效率优化的SPLADE++模型独立实现。
库名称:transformers
管道标签:填充掩码
面向工业场景的SPLADE++模型独立实现(又称splade-cocondenser*
及其衍生版本)
本工作基于两项坚实研究:Naver团队的《从蒸馏到硬负采样:让稀疏神经IR模型更有效》论文和Google的SparseEmbed。向两个团队的卓越工作致敬。
1. 什么是稀疏表示?为何要学习它?
初学者? 展开阅读。熟悉稀疏与稠密表示? 可直接跳至第2节。
1. 词法搜索:
基于词袋(BOW)的稀疏向量词法搜索是强基线方法,但存在词汇不匹配问题,因其仅支持精确词项匹配。优缺点如下:
- ✅ 高效且成本低
- ✅ 无需微调模型
- ✅️ 可解释性强
- ✅️ 精确词项匹配
- ❌ 词汇不匹配(需记忆精确词项)
2. 语义搜索:
基于近似最近邻搜索的神经/稠密检索器(DPR、Sentence Transformers*、BGE*模型)表现出色。优缺点如下:
- ✅ 模拟人类自然思维方式
- ✅ 微调后性能远超稀疏方法
- ✅ 易于多模态扩展
- ❌ 存在词符遗忘(忽略词项匹配)
- ❌ 资源密集(索引与检索)
- ❌ 可解释性差
- ❌ 需针对OOD数据微调
3. 核心思想:
结合两种搜索的优势催生了学习稀疏表示的研究兴趣,这种表示兼具可解释性,同时作为查询和文档的隐式/显式(潜在、上下文感知)扩展机制。若需了解查询扩展,推荐阅读Daniel Tunkelang的权威资料。
4. 稀疏模型的学习目标:
模型通过MLM头将稠密表示投影为词汇分布,实现自动词符扩展。(图示由pinecone提供)
2. 动机:
SPLADE模型在检索效果(质量)与检索效率(延迟与成本)间取得平衡。我们进行了轻微的效率优化以适配工业场景。
(纯MLE研究者需注意:此处效率特指检索效率而非模型推理效率,后者将后续讨论。)
核心优化与成果:
- FLOPS调控:采用独立序列长度与严格FLOPS限制,文档(128)和查询(24)词符预算(官方SPLADE++为256),灵感源自SparseEmbed
- 权重初始化:使用bert-base-uncased原始权重,不同于官方SPLADE++/ColBERT的语料感知初始化
- 性能表现:在ID数据上MRR@10达37.22(OOD数据48.7),检索延迟47.27ms(多线程),仅用5个负样本/查询,运行于消费级GPU
- 工业适配:证明"SPLADE++不适合单CPU检索"的观点不成立,且领域定制需超越牺牲FLOPS换取微小增益的策略
- 因查询时延考量,仍需分离查询/文档模型,当前为文档模型,查询模型即将发布
注:论文将最佳性能模型称为SPLADE++,为保持一致性我们沿用此命名
3. 为何FLOPS是工业场景关键指标?
通过实例分析可见,我们的模型在比同类SPLADE++模型(包括SoTA)少10%~100%词符的情况下达到相当效果。
(定量分析见下节)
目标定位:我们并非追求"超越SoTA MRR",而是探索"以何种成本实现可接受的MRR@10"。盲目降低λ值虽可提升MRR,但会导致更高FLOPS、更多词符和更低效率——这不符合工业需求。
我们的输出(词符数:113)与对比模型:
- naver/splade-cocondenser-ensembledistil(SoTA,词符多10%,FLOPS=1.85)
- naver/splade-v2-distil(词符多100%,FLOPS=3.82)
注1:本例选自对比示例以方便比较
4. 实证指标表现
我们的模型在保持词符稀疏性的同时具备高效检索能力,体现为更快的检索速度(用户体验)和更小的索引体积(成本)。标准MS-MARCO小开发集的平均检索时间和总FLOPS损失对比如下:

注:为何选用Anserini而非PISA?
Anserini是基于Lucene的生产级库,工业搜索部署多使用Solr/Elasticsearch等Lucene衍生系统,性能可比。PISA延迟对工业无参考价值,因其仅为研究系统。
完整Anserini评估日志包含编码、索引和查询细节。
其他差异点:
- Cocondenser权重:不同于官方SPLADE++或SparseEmbed,我们未使用Luyu/co-condenser*模型初始化,但仍达到同等性能
- 模型尺寸:官方SPLADE++、SparseEmbed与本模型均基于相同尺寸的
bert-base-uncased
微调
5. 工业适配路线图
- 效率提升:持续优化服务与检索效率
- 领域微调:虽然SPLADE零样本OOD表现优异,但工业场景需支持自定义数据微调。当前SPLADE微调成本高,我们将探索低成本无监督微调方案
- 多语言SPLADE:基于mBERT或XLMR的多语言SPLADE训练成本与词汇量(12万/25万 vs bert-base的3万)正相关,我们将研究扩展方案
6. 使用方法
为提供轻量级推理方案(无PyTorch依赖),我们将发布SPLADERunner库。当然也可直接使用HuggingFace transformers。
使用指南
6a. 主流向量数据库集成
向量数据库 |
Colab链接 |
Pinecone |
 |
Qdrant |
待补充 |
6b. 使用SPLADERunner库
pip install spladerunner
from spladerunner import Expander
expander = Expander()
sparse_rep = expander.expand(["曼哈顿计划及其原子弹加速了二战结束。其原子能和平利用的遗产持续影响历史与科学。"])
6c. 使用HuggingFace
Notebook用户需先登录
!huggingface-cli login
代码集成参考
HF令牌使用指南
修改如下参数:
tokenizer = AutoTokenizer.from_pretrained('prithivida/Splade_PP_en_v1', token=<您的令牌>)
model = AutoModelForMaskedLM.from_pretrained('prithivida/Splade_PP_en_v1', token=<您的令牌>)
完整代码示例
import torch
from transformers import AutoModelForMaskedLM, AutoTokenizer
device = "cuda:0" if torch.cuda.is_available() else "cpu"
tokenizer = AutoTokenizer.from_pretrained('prithivida/Splade_PP_en_v1')
reverse_voc = {v: k for k, v in tokenizer.vocab.items()}
model = AutoModelForMaskedLM.from_pretrained('prithivida/Splade_PP_en_v1').to(device)
inputs = tokenizer("文本示例", return_tensors='pt')
inputs = {k: v.to(device) for k, v in inputs.items()}
outputs = model(**inputs)
relu_log = torch.log(1 + torch.relu(outputs.logits))
weighted_log = relu_log * inputs['attention_mask'].unsqueeze(-1)
vector = torch.max(weighted_log, dim=1)[0].squeeze()
cols = vector.nonzero().squeeze().cpu().tolist()
weights = vector[cols].cpu().tolist()
bow_rep = [(reverse_voc[k], round(v,2)) for k,v in zip(cols, weights)]
print(f"实际维度数: {len(cols)}\nSPLADE词袋表示:\n{sorted(bow_rep, key=lambda x: -x[1])}")
BEIR零样本OOD性能
训练细节
待补充
致谢
- 感谢Nils Reimers的宝贵建议
- 感谢Anserini库作者
局限性与偏差
BERT模型的所有局限性均适用于本微调模型
引用
若使用我们的模型或库,请引用:
Damodaran, P. (2024). Splade_PP_en_v1: 面向工业场景的SPLADE++模型独立实现(版本1.0.0)[计算机软件]。