首页 / 文章 / 您需要自定义嵌入模型吗?
← 返回
IT技术

您需要自定义嵌入模型吗?

✍️ zhirenhun 📅 2026/8/25 👁 107 阅读 ⏱ 21 分钟
您需要自定义嵌入模型吗?

大多数构建 RAG 的团队应该直接使用现成的嵌入模型,然后继续前进。OpenAI 的 text-embedding-3-large 和 Cohere 的 embed-v4 价格低廉(大约每百万 token 0.12 到 0.13 美元),文档齐全,对大多数语料库已经足够。但有一个具体且可衡量的点,这时候情况会改变:如果你在自有领域查询上的 recall@10 使用通用模型时低于约 80%(也就是说,在 10 次测试查询中,有少于 8 次的正确文档能进入前 10 名),并且你能够收集几千个真实的查询‑文档对,那么自行微调嵌入模型通常能在准确率和成本上都超过 API。 那些在 此类微调中发表结果 的团队表示,一个经过微调的小型开放模型可以在指标上超过 OpenAI 或 Cohere 的成绩 10 分或更多。而且训练的成本还不到一顿午餐的钱。

为什么通用嵌入在狭窄领域会失效

通用嵌入模型是在广泛的网络文本上训练的。它们很擅长认识到 “car” 和 “automobile” 在向量空间中很接近。但在你的支持系统里,它们很难意识到 “ORA-01555” 和 “snapshot too old” 实际上是同一个问题,或者在你的代码库中,reconcile_ledger 和 “nightly balance job” 指的是同一件事。

这是一个词汇问题,微调可以直接解决。对比式微调会从你的数据中取出(查询,正确文档)对,将它们的向量拉近,同时把无关文档推远。模型整体并不会变得更聪明。它在一件事上会变得更好:把用户提问的表达方式映射到文档回答的表达方式。这就是检索嵌入的全部工作,这也是为什么即使是小模型也能带来显著提升。

下面的具体例子——也是后面工作示例所用的场景——是一个工单票据 RAG 系统。用户提交的工单往往语言混乱、缩写很多(比如 “db conn pool exhausted after upgrade”)。而知识库则使用正式的产品语言编写(例如 “Connection limits in Managed Databases”)。通用模型在这两段文字之间几乎找不到重叠。但在六个月的已解决工单上进行微调的模型——每张工单都与解决它的文章配对——会把它们视为近邻。

一些术语,通俗解释

  • 嵌入向量: 一串数字,用来表示一段文本的含义。相似的文本对应相似的数字。
  • 知识库: 你要搜索的文档、文章或文本片段的集合。在下面的例子中,它是一组支持文章。
  • 向量数据库: 用于存储嵌入向量并快速查找与给定查询最接近的向量的数据库。pgvector 是内置于 Postgres 的向量数据库。
  • Recall@10: 在所有测试查询中,正确文档出现在前 10 名结果中的比例。数值越高越好。
  • MRR (平均倒数排名): 在所有查询中,正确文档在结果列表中的排名位置的平均倒数。正确答案排名第 1 得分高于排名第 5。
  • 对比微调: 通过在“此查询匹配此文档”的配对上训练模型,使其学习将匹配的配对在向量空间中放得更近。

比较看起来是什么样子?

设置。 这是您在自己的系统中应该运行的比较,基于上面的支持票场景进行演示:10,000 个知识库块,1,000 个保留的测试查询(在训练过程中被隔离且从未展示给模型,仅用于验证其答案)带有标注的正确文档,以及 50,000 个训练配对(每个配对是一个真实的支持问题与解决它的文章匹配),这些配对来源于已解决的票据。关于这些数字来源的简要说明。

召回率和 MRR 行(如上所述:正确答案出现的频率以及它排名靠前的程度)是其他团队在进行此类微调后报告的典型结果。维度、存储和成本行只是事实:它们直接来源于模型规格和各提供商的标价,因此这些与您的数据无关。如果您想要针对自己的系统得到一个数值,则需要自行运行测试,本文末尾的框架会指导您完成此过程。

text-embedding-3-large (API) bge-base-en-v1.5 (现成的) bge-base-en-v1.5 (微调后)
召回率@10 (领域查询) 0.71 0.66 0.83
MRR@10 (领域查询) 0.58 0.52 0.71
召回率@10 (通用查询) 0.89 0.84 0.82
向量维度 3,072 768 768
10M 块的存储 (pgvector) ~123 GB ~31 GB ~31 GB
嵌入成本,10M 块 ~$650 (API)* GPU 时间 GPU 时间
查询延迟(嵌入 + 搜索,p50) ~180 ms ~45 ms ~45 ms

*假设每块 500 个标记,标准费率为 $0.13/M。如果您可以等待最多 24 小时,Batch API 可将此费用减半至约 $325;我们在 RAG 管道成本分解 中使用了相同的假设,以便两篇文章保持可比性。

在此表格中,有三点值得诚实地说明。

首先,在领域查询上,微调模型以显著优势获胜,召回率大约高出 12 个百分点@10,远超更大的 API 模型。这表明词汇差距在缩小,这与类似语料库上发表的微调模型的报告一致。其次,在通用英文查询上可能会稍有下降。微调是在广度和深度之间的权衡。如果你的查询主要是通用问题,微调模型可能达不到要求,这时应停止。第三,延迟和存储的提升与微调无关。它们来源于自行运行一个小模型:使用 768 维而不是 3072 维,意味着 pgvector 存储和索引大小仅为原来的四分之一,查询时无需往返 API 的网络开销。即使不对 bge-base 进行微调,自行托管也能获得这些好处。

训练过程本身规模较小。bge-base-en-v1.5 拥有 1.09 亿参数。在单个 H100 GPU Droplet 上对 5 万对数据进行微调大约需要 40 分钟,按小时计费,因此运行成本约为 3.40 美元。它也能跑在更便宜的卡上,比如 RTX 4000 Ada,此时同样的运行成本低于 1 美元。此项目的主要成本从来不是 GPU,而是收集高质量的查询‑文档对。

运行涉及的内容

此部分旨在展示训练过程规模小且可复现,而非教程。完整的演练步骤应放在文档中;这里是上述比较的参考实现。

训练数据是从已解决的工单中提取的正样对组成的 JSONL 文件:

{"query": "db conn pool exhausted after upgrade", "positive": "Connection limits in Managed Databases: each plan tier has a maximum connection count..."}
{"query": "how do i rotate the ca cert", "positive": "Rotating certificates: Managed Databases uses a per-cluster CA that can be regenerated..."}

下面的代码大约有 30 行,使用了 sentence-transformers 库。它采用了一种称为 MultipleNegativesRankingLoss 的训练方法,其工作原理如下:在每个训练批次中,针对每个查询,其对应的正确文档即为正确答案,而同一批次中的其他所有文档会被自动视为错误答案。这就是全部的技巧,也是为什么你只需要收集正确的配对。错误的示例会随着批次中其它内容自动出现,无需额外获取。

from datasets import load_dataset
from sentence_transformers import (
    SentenceTransformer,
    SentenceTransformerTrainer,
    SentenceTransformerTrainingArguments,
)
from sentence_transformers.losses import MultipleNegativesRankingLoss

model = SentenceTransformer("BAAI/bge-base-en-v1.5")

dataset = load_dataset("json", data_files="pairs.jsonl", split="train")
dataset = dataset.rename_columns({"query": "anchor", "positive": "positive"})

args = SentenceTransformerTrainingArguments(
    output_dir="bge-base-support-ft",
    num_train_epochs=2,
    per_device_train_batch_size=64,   # larger batches = more in-batch negatives
    learning_rate=2e-5,
    warmup_ratio=0.1,
    bf16=True,
)

trainer = SentenceTransformerTrainer(
    model=model,
    args=args,
    train_dataset=dataset,
    loss=MultipleNegativesRankingLoss(model),
)
trainer.train()
model.save_pretrained("bge-base-support-ft/final")

评估使用同一库内置的 InformationRetrievalEvaluator,它直接针对保留的查询集报告 recall@k 和 MRR。存储和搜索在带有 HNSW 索引的托管 Postgres 上的 pgvector 上运行;微调模型不会改变你存储或查询它的方式,因此该设置正是 pgvector 文档所描述的。

模型选择在 Postgres 中仅在大小方面体现。在 768 维度下,1000 万个块大约需要 31 GB 的向量加索引。在 3,072 维度下,相同的语料库大约需要 123 GB,这会在你存储任何应用数据之前就迫使你升级到更大的数据库方案。这种差异每月都会重复出现,而训练成本则是一次性的。

其他平台在嵌入微调方面的立场

如果你在决定将此部署到哪里,诚实的对比如下。

Together AI 和 Fireworks 都提供托管的微调服务,但它们支持的模型列表仅限于生成式模型:用于聊天、视觉和推理的 Llama、Mistral 和 Qwen 系列。就目前而言,两者均未将嵌入模型列为可微调的;嵌入仅作为推理端点提供。这一点很重要,因为本文的重点是训练嵌入模型,而不是提供服务。

Baseten 的训练文档 描述了两条路径:Loops 用于监督微调,Training Jobs 用于运行你自己的训练代码;两者都会生成可直接部署到其推理栈的检查点。在服务方面,Baseten 嵌入推理 是一个专门用于嵌入、重排和分类模型的引擎,Baseten 报告其吞吐量超过次佳解决方案的 2 倍,延迟降低约 10%。如果你希望从训练作业到服务端点拥有一条托管流水线,这也是一个可供评估的公平选项,尽管需要支付相较于原始算力的平台溢价。

Modal 是带你自己的训练作业方式的最接近的比较,他们已经发布了完全相同的工作流程:微调一个开放的嵌入模型以击败专有 API。区别在于计算的形态。Modal 提供按秒计费的 GPU 的无服务器函数,这对于突发的、并行的实验扫描非常合适。

GPU Droplet 只是一个普通的虚拟机:你通过 SSH 登录,运行上面的脚本,按小时付费,而你的向量存放在已经保存应用数据的同一个 Postgres 中。如果你已经在运行定期重新训练任务和生产数据库,那么 VM 加上托管 Postgres 的方案更简单。若需要进行数百次并行的超参数搜索,Modal 的方案更合适。两种方案都是可行的;根据你重新训练的频率来选择。

值得在这里直接提到 DigitalOcean,因为它正是上述数字背后的基础设施。GPU Droplets 是原始的计算资源,而不是托管的微调产品:你会得到一个已经安装好 GPU 驱动的虚拟机,并在其上运行自己的训练脚本,这正是本文训练过程所做的。这里没有类似 Baseten 的 Loops 或 Together 的微调端点那样的 “提交微调作业” API(用于聊天模型)。你得到的而是对该虚拟机周围整个栈的完全控制:你想要的任何训练脚本、带有 pgvector 的托管 Postgres 用于存储得到的向量,以及可以在同一账户内共存的训练运行和生产数据库,无需经过独立的平台。

OpenRouter 不属于此比较范围。它只是一个位于推理 API 之上的路由层,本身不进行任何训练,因此它只能为你提供他人的嵌入模型,而无法帮助你构建自己的模型。

何时可以跳过微调

在以下三种情况下,你可以毫无愧疚地跳过微调。

查询量低。训练成本很低,但运营成本不低:有人需要负责配对管道、评估集和重新训练计划。如果你的系统每天只服务几百次查询,且检索基本可用,那么这种所有权成本永远得不到回报。

词汇变化快。如果你的领域术语每月都在变化(新产品名称、新错误码),你的微调模型总是略显过时,重新训练的频率就会成为反复征收的税费。在这里,通用模型加上良好的关键词或混合搜索往往更持久。

没有标注的配对,也没有获取它们的途径。模型的好坏仅取决于你用于训练的查询‑文档配对。如果你无法从日志、工单或点击数据中挖掘这些配对,而只能从文档自身生成合成配对,那么相比上面的表格,提升幅度会小很多。合成配对只能让模型学到你文档的词汇,而不能学到用户的词汇,而这两者之间的差距正是你本来想要弥补的。

决策框架:证明微调合理的三个条件

决定前先进行测量。构建一个包含 200 到 500 条带有正确文档标签的真实查询的测试集,运行您当前的嵌入模型,并计算 recall@10。然后:

如果以下三个条件全部满足,则进行微调:领域查询的 recall@10 低于约 80%,您可以组织至少几千个真实的查询‑文档对,且团队中有人可以负责一个再训练周期(对于大多数领域来说,每季度一次就足够了)。

如果满足以下任意条件,则继续使用 API:您的查询主要是通用英语,您的词汇变化速度快于您重新训练的速度,或者您没有真实配对的来源。

如果您实际需要的是更低的延迟或更小的向量,则在不进行微调的情况下自行托管。在您自己的 GPU 上使用股票开放模型即可同时获得这两者,并且在您拥有配对后可以以后再添加微调。

这并不是一个难以做出的决定。构建评估集,花一个下午进行测量,您就会知道自己处于哪一方。这比猜错便宜得多——无论是被锁定在您不需要支付的 API 账单上,还是构建您不需要的训练基础设施。

——

🧑‍💻

zhirenhun

一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。

← 上一篇
使用 DigitalOcean 批量推理对 250,000 条记录分类,成本仅 7.04 美元
下一篇 →
我尝试对自己的 Agent 引擎进行提示注入,未成功,原因如下。

📌 相关推荐

GraphRAG 是推理问题,而非数据库问题
2026/8/30
构建市场时光机:使用 Python 和 WebSocket 重放交易会话
2026/8/30
如何自行基准测试LLM推理:值得信赖的数字设计标准
2026/8/30
← 返回文章列表