资讯动态

嵌入维度越高,固定阈值越不能用

发布时间:2026/10/20 10:05:09 来源:尧图企业网站定制
版权与内容来源声明本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容均在附表 A 中标注来源引用官方原文保持原样不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准标注「待验证」的部分请以你本地环境实际输出为判断依据。本文不推荐任何不合规的软件获取方式也不对任何收益结果作承诺。转载请注明出处。一、这句话的问题不在数字在它默认了一件事先把三个词翻译成人话。向量就是一串有序的数用来代表一段文本维度就是这串数有几个余弦相似度是把两个向量都当成从原点出发的箭头量它们夹角有多小——同方向接近 1垂直是 0反方向是 -1。「相似度 0.8 以上算相关」这句经验隐含了一个很强的假设分数的刻度是通用的。就像温度一样20 摄氏度在谁的体温计上都是 20 摄氏度。但相似度不是温度。它没有绝对零点也没有统一单位。0.8 到底算「很像」还是「勉强」取决于这个模型把向量撒在空间里的什么位置。1.1 三个变量任意一个变0.8 的含义就变变量它改变了什么结果维度随机分数的分布宽度维度越高分布越窄0.8 越难达到共享偏移无关文本的分数基线基线抬高后0.8 可能已经是「很松」的线是否归一化排序依据是方向还是方向×长度同一批候选Top10 可以换掉四个这三件事不是三个独立的小毛病它们会叠加。本文剩下六章就是把这三次叠加拆开看一遍然后给出一套「自己测」的最小流程。1.2 先把实测边界说清楚本文所有数字来自work/2026-10-19_嵌入维度阈值实测.py环境 numpy 2.5.2全部是纯几何模拟随机向量、人为设定的分布。它能证明的只有几何事实——维度如何改变分布宽度、偏移如何抬高基线、归一化如何改变排序。它不能告诉你任何具体模型的语义分数是多少也不能被当成「某模型的真实阈值参考」。真实阈值只能在你自己的语料上采样统计。1.3 结论先放这儿阈值不是模型的属性是「你的语料 你的模型 你能承受的错」三者的联合属性。换掉其中任何一个就得重测一遍。二、第一件事维度一高分数就挤到 0 附近2.1 实测标准差从 0.3556 掉到 0.0180同一套生成方式只改维度看两个随机向量的余弦长什么样。每档 4000 对命中比例那一列用 20000 对维度余弦均值标准差理论值 1/√d随机对 ≥0.8 的比例80.00300.35560.35360.52%320.00080.17690.17680.0000%1280.00310.08830.08840.0000%7680.00010.03590.03610.0000%1536-0.00040.02540.02550.0000%3072-0.00020.01800.01800.0000%均值全在 0 附近标准差一路收缩。这不是巧合是数学上的必然。2.2 为什么必然高维里两支随机箭头几乎垂直设 x、y 是 d 维独立标准正态随机向量各自归一化后求余弦 x·y 是 d 个独立项之和每项方差为 1所以标准差 ≈ √d 归一化后 |x| ≈ √d|y| ≈ √d 于是 cos ≈ (x·y) / d标准差 ≈ √d / d 1/√d d8 → 1/√8 ≈ 0.354 d768 → 1/√768 ≈ 0.036 d3072 → 1/√3072 ≈ 0.018拿这个式子去对上面那张表实测和理论差在小数点后第三位属于抽样波动。所以「维度升高、分布收窄」不是某个库的实现问题是几何本身的性质维度越高两个随机方向就越接近正交剩下那点偏差被摊得越来越薄。这就是「维度灾难」的一个具体面——不是算不动是区分度被摊薄了。分数全都挤在 0 附近1% 分位和 99% 分位之间的跨度从 8 维的约 1.48 缩到 3072 维的约 0.083。2.3 后果0.8 在不同维度下不是同一个严格程度看表格最后一列。8 维时0.52% 的随机对能过 0.832 维及以上一对都过不了0.0000%。反过来说如果你在低维空间里习惯了「0.8 才算相关」换到 1536 维0.8 就变成了「几乎只有同一个向量才够得着」——阈值实际被收紧了一个数量级以上。如果模型训练得不充分、分数整体偏低0.8 又可能一条都过不了。真正的问题不是 0.8 太高或太低是它没有可比的参照系。分布一窄绝对阈值携带的信息量就掉了这时候该看的是「这个分数在这批候选里排第几」而不是「它够不够 0.8」。三、第二件事真实模型的分数不是以 0 为中心的3.1 一手论文里怎么说这个现象一般叫各向异性向量不是均匀铺满整个球面而是挤在一个狭窄的锥形区域里方向都差不多。《On the Sentence Embeddings from Pre-trained Language Models》Bohan Li 等EMNLP 2020arXiv:2011.05864摘要原文写We find that BERT always induces a non-smooth anisotropic semantic space of sentences, which harms its performance of semantic similarity.再往前《Representation Degeneration Problem in Training Natural Language Generation Models》Jun Gao 等ICLR 2019arXiv:1907.12009摘要原文写most of the learnt word embeddings “tend to degenerate and be distributed into a narrow cone, which largely limits the representation power of word embeddings”。两篇都在 2026-10-19 直连 arXiv 摘要页核到原文。需要说清的是这两篇研究的是特定模型、特定训练目标下的现象不能读成「所有嵌入模型都必然如此」更不能读成「有偏移就一定坏」——有的模型就是靠着偏移把同义句聚得更紧。3.2 几何上的后果给所有向量加同一个方向模拟方式先撒 20000 个随机单位向量再统一加一个方向的偏移重新归一化然后看相邻两条互不相关条目的余弦。维度偏移 0偏移 0.5偏移 1.0≥0.8 的比例三档偏移1280.00080.19940.4998均 0.00%768-0.00000.19980.4999均 0.00%15360.00020.19990.4999均 0.00%三个维度的结果几乎一样说明这件事和维度无关是偏移本身在起作用。3.3 读这张表要读出两件事第一无关文本的「地板」被抬起来了从 ≈0 抬到 0.2 左右再到 0.5 左右。偏移到 1.0 这一档时0.5 这条线已经切在无关文本分布的正中间而不是尾巴上——你在 0.4 设阈值大部分条目都会过线按老经验在 0.5 上下设等于把一半的无关文本也放进来。这已经是另一套刻度了。第二别把这张表读过头。即便偏移 1.0≥0.8 的比例仍然是 0.00%。这说明偏移单独不足以把分数顶到 0.8但足以让「低分区」的所有阈值集体失效。真实模型是偏移、训练目标、维度三者一起作用不能拿单因素模拟去外推具体数值——所以本文不给任何具体模型的推荐阈值。四、第三件事归一化与否改变的是排序4.1 实测Top10 只重合 6 个模拟 200 条候选每条先随机生成、再乘一个随机的模长系数模拟真实语料里文本长度、信息密度的差异然后用同一个查询去检索点积 Top10 与余弦 Top10 重合6/10点积 Top10 平均模长37.11余弦 Top10 平均模长14.33全体 200 条平均模长15.94点积挑出来的是「长的那批」平均模长比全体均值高出一倍多余弦挑出来的是「方向最像的那批」平均模长还略低于全体。打分方式依什么排序实测 Top10 平均模长什么时候用它点积未归一化方向 × 模长37.11模型训练时就用点积且模长本身携带信息余弦只看方向14.33语料长度差异大或模型没针对点积训练归一化后点积只看方向与余弦等价14.33想要余弦的效果又想要点积的速度4.2 归一化之后点积就是余弦自检结果在同一批归一化向量上分别算点积和余弦逐元素相减的最大绝对误差是0.0。这不是「近似相等」是同一个算式。同一逻辑的最小写法如下与实测脚本第 3 段同构。种子不同具体数字会和上面对不上这段的用途是看结构——重点看scale那一行去掉它两种排序会完全一致什么也测不出来。⚠️代码待验证importnumpyasnp rngnp.random.default_rng(20261019)Arng.normal(size(200,128))Qrng.normal(size(1,128))scalenp.exp(rng.normal(0,0.8,size(200,1)))# 模拟真实语料里的模长差异AA*scale dot(A Q.T).ravel()# 未归一化方向 × 模长AnA/np.linalg.norm(A,axis1,keepdimsTrue)QnQ/np.linalg.norm(Q,axis1,keepdimsTrue)cos(An Qn.T).ravel()# 归一化后只看方向top_dotnp.argsort(-dot)[:10]top_cosnp.argsort(-cos)[:10]print(Top10 重合:,len(set(top_dot)set(top_cos)),/10)print(点积 Top10 平均模长:,round(float(np.linalg.norm(A[top_dot],axis1).mean()),2))print(余弦 Top10 平均模长:,round(float(np.linalg.norm(A[top_cos],axis1).mean()),2))print(全部平均模长:,round(float(np.linalg.norm(A,axis1).mean()),2))官方文档把这件事写得很直接。Sentence Transformers 的语义文本相似度页面写「Dot product on normalized embeddings is equivalent to cosine similarity, but “cosine” will re-normalize the embeddings again. As a result, the “dot” metric will be faster than “cosine”.」截至 2026-10-19这句话里有个实用的细节归一化向量上再用 cosine等于又归一化了一遍白算一次。所以已经归一化的库选点积更快结果一样。4.3 换维度就是换坐标系排序不能继承再看一个更极端的同一批查询和候选截取前 128 维算一次 Top10截取前 1536 维再算一次重合0/10。这是随机数据只能说明「排序不可继承」不能当作真实模型的结论——真实的 1536 维模型通常比 128 维更强。但它指向一个工程事实换模型、换维度之后原来那套「top-k 取前 5 条」的经验、原来那些调好的阈值全部作废得重来。五、三件事叠在一起0.8 到底意味着什么5.1 一组人为设定的分布下面这组数据是为了看清「阈值怎么移动代价」而人为设定的同义对服从均值 0.82、标准差 0.04 的分布无关对服从均值 0.30、标准差 0.10 的分布各抽 5000 个。这两个均值是示意值不是行业经验值也不是任何模型测出来的。阈值同义被漏判无关被误判0.700.04%0.00%0.753.54%0.00%0.8030.10%0.00%0.8577.00%0.00%0.9097.72%0.00%5.2 这是代价曲线不是准确率曲线先看「同义被漏判」那一列。阈值 0.90 时97.72% 的同义对被判成不相关——系统看起来「几乎不返回结果」很像在追求精确实际上是残废该给模型看的资料十份里有九份没给。再看「无关被误判」那一列。五档全是 0.00%但这个 0不代表安全它只说明这组人为设定里两个分布分得足够开。真实语料里无关对被误判的比例不会是 0这也是为什么不能拿这张表当结论。这张表真正能教给人的只有一件事同一个模型内阈值每挪 0.05「漏」和「错」就是数量级的交换。你在 A 模型上把 0.8 调成 0.75感觉好用了换到 B 模型同样的 0.75 可能落在完全不同的位置——因为 B 的分布宽度、基线位置、排序依据都变了。5.3 阈值不是找「最像的那一点」是选「你能承受哪种错」RAG 场景里两种错的代价不一样漏判该召回没召回模型答「资料里没有」或者拿错资料硬答——用户当场就能感觉到。误判不相关被召回噪声进上下文模型被带偏——更隐蔽但往往更贵因为错得很自信。所以顺序应该是先定「哪种错更不能接受」再定阈值往哪边偏。取两个分布的中点是最省事的做法也是最没有依据的做法——它默认了两种错的代价相等而现实里几乎从不如此。举两个方向相反的例子。客服知识库漏判的代价是用户当场觉得「它不知道」误判的代价是答案沾点边、但还糊得过去——偏松一点更划算。合同审查库漏判会漏掉一条风险条款误判只是让人多看两页——这时候该偏严宁可多召回再靠重排和模型判断往下筛。同一个阈值 0.8在这两个场景里不该是同一个数甚至不该是同一个数量级的严格程度。RAG 检索参数自查表把阈值、top-k、归一化策略、分数分布四项做成了一张可填写的对照表配合本章的代价表直接填数。放在资料包里扫码即可获取六、自己测阈值的最小流程6.1 四步一步都不能省采样正负样本对。从你自己的语料抽 100–300 对「确实相关」同一段落的相邻句、问题和它对应的答案段再抽同样数量的「确实无关」随机跨文档配对。量不用大但要覆盖你真实的文本形态——短查询配长段落、表格片段、代码块都放一点进去。各算一次分布。两组分别算余弦看均值、标准差、5% 和 95% 分位。只看均值会漏掉最关键的信息尾部长什么样。找交叠区。正样本的低分尾和负样本的高分尾在哪个区间重叠。重叠区越宽任何单一阈值都做不好这时候要考虑的是换模型或加重排而不是继续调那个数。用代价定阈值。在交叠区里挑挑的依据是「漏判和误判哪个更贵」不是「哪个数好看」。6.2 一段可以改成自己用的统计脚本先约定两个输入emb是「文本 id → 向量」的字典由你自己的模型生成same_pairs和diff_pairs是两组 id 对自己从语料里抽。脚本只负责统计不负责生成向量。⚠️代码待验证importnumpyasnpdefcos(a,b):aa/np.linalg.norm(a)bb/np.linalg.norm(b)returnfloat(a b)samenp.array([cos(emb[a],emb[b])fora,binsame_pairs])diffnp.array([cos(emb[a],emb[b])fora,bindiff_pairs])forname,sin((相关对,same),(无关对,diff)):print(f{name}均值{s.mean():.4f}标准差{s.std():.4f}f5%{np.quantile(s,0.05):.4f}95%{np.quantile(s,0.95):.4f})print(交叠区下沿相关对 5% 分位:,round(float(np.quantile(same,0.05)),4))print(交叠区上沿无关对 95% 分位:,round(float(np.quantile(diff,0.95)),4))fortinnp.arange(0.40,0.95,0.05):miss(samet).mean()fp(difft).mean()print(f阈值{t:.2f}: 漏判{miss*100:.2f}% 误判{fp*100:.2f}%)三个容易做错的地方比脚本本身更重要正负样本对要来自同一个切分策略。入库时按 500 字切块、测试时拿整篇文章去配分布根本不可比。正样本对不能全是「几乎同一句话」。那样会把正样本分布拉得过于乐观测出来的阈值上线就出事。阈值写进配置时要带上模型名和测量日期。三个月后有人换了模型没换阈值至少还能查到源头。6.3 两条硬规则规则一入库前归一化并且先确认向量库自己怎么算。不同向量库对 cosine 的处理并不统一。Qdrant 官方文档写「For search efficiency, Cosine similarity is implemented as dot-product over normalized vectors. Vectors are automatically normalized during upload.」截至 2026-10-19意思是它会在上传时自动归一化你不需要自己做。但别的库、别的索引类型未必如此——自己手写一遍归一化不亏因为它是幂等的归一化过的向量再归一化一次还是它自己。规则二查询向量必须和入库向量同一个模型、同一个版本。这一条没有例外。不同模型的向量空间之间没有对应关系维度一样也不行。换模型等于换坐标系必须全库重算不是「改个配置项」。最常见的翻车就是入库用的是旧模型查询侧 SDK 升级后默认拉了新模型两套向量算出来的分数看着像模像样其实毫无意义。七、怎么知道阈值配错了7.1 四个信号对应四种不同的病现象更像是什么问题先动哪里召回为空top-k 全被阈值砍掉阈值过高或查询与入库不是同一个模型打印 top-20 的分数看最高分到底是多少全库都是高分排序没有区分度共享偏移把地板抬起来了看均值与分位改看相对排名而不是绝对值分数挤在很窄的区间如全在 0.78–0.84分布收窄绝对阈值失效换成取 top 5% 的分位数阈值排序稳定但答案层总出错不是阈值问题查切分粒度、召回条数、有没有重排最后一行值得多说一句。很多人把「答案不对」归咎于阈值但阈值只决定「给不给模型看」不决定「模型看懂没看懂」。排序稳定说明检索这层没坏该往上查切分是不是把一个论点拆成了两半、召回 3 条是不是太少、有没有加一层重排。还有一个几乎不花成本的办法把每次检索的 top-1 分数、top-k 里的最低分、命中条数写进日志。跑一周你就有了线上真实分布——它和你手写的测试集从来不一样因为用户问问题的方式和你造样本时想的不一样。7.2 为什么换模型必须重测四个变量同时变了一个都不剩维度变了 → 分数分布的宽度变了第二章。归一化策略变了 → 排序依据从「方向×长度」变成「方向」或者反过来第四章。共享偏移变了 → 无关文本的基线位置变了第三章。训练目标变了 → 「相关」这个词本身的定义变了。对比学习训出来的模型同一个 0.5 在一个模型上可能比在另一个模型上的 0.8 更有分量。所以「我们以前用 0.8 挺准的」这句话跨模型说没有意义。它甚至不能跨语料说同一模型把中文语料换成中英混排分布也会动。7.3 一句话结论0.8 不是错错的是把它当成通用刻度。阈值应该落在这两条分布的交叠区里按你能承受的错来选——维度让分布变窄偏移让地板抬高归一化让排序换了依据三件事任何一件变了那个 0.8 就已经不是原来那个 0.8。真要落地就四步采样本、算分布、找交叠、按代价定。花半天测一次比抄一个数字省事得多。嵌入模型选型与阈值记录表把模型名、维度、归一化方式、正负样本分布、最终阈值和测量日期记在同一张表上换模型时有据可查。放在资料包里扫码即可获取附表 A本文引用事实与出处对照表事实出处文档名 发布方 链接本文位置运行环境 numpy 2.5.28/32/128/768/1536/3072 维随机向量余弦的标准差依次为 0.3556、0.1769、0.0883、0.0359、0.0254、0.0180均值均在 0 附近本机模拟work/2026-10-19_嵌入维度阈值实测.py与.lognumpy 2.5.2每档 4000 对第 2.1 节随机对 ≥0.8 的比例8 维 0.52%32 维及以上 0.0000%本机模拟同上第 2 段每档 20000 对第 2.1、2.3 节点积 Top10 与余弦 Top10 重合 6/10点积 Top10 平均模长 37.11、余弦 Top10 平均模长 14.33、全体 200 条平均模长 15.94本机模拟同上第 3 段128 维、200 条候选第 4.1 节归一化向量上点积与余弦的逐元素最大绝对误差为 0.0本机模拟同上第 4 段自检第 4.2 节128 维与 1536 维各算 Top10重合 0/10随机数据仅说明排序不可继承不代表真实模型本机模拟同上第 5 段第 4.3 节阈值示意表0.70→漏判 0.04%、0.75→3.54%、0.80→30.10%、0.85→77.00%、0.90→97.72%误判各档均为 0.00%所用分布为同义对 0.82±0.04、无关对 0.30±0.10本机模拟同上第 6 段两组分布为人为设定的示意值非行业经验值不代表任何模型第 5.1、5.2 节共享偏移 0 / 0.5 / 1.0 时互不相关条目的余弦均值从 ≈0 升到 0.1994–0.1999 再到 0.4998–0.4999128/768/1536 维结果一致三档偏移下 ≥0.8 的比例均为 0.00%本机模拟同上第 7 段第 3.2、3.3 节随机单位向量余弦的标准差约等于 1/√d本文自行推导x·y 的方差为 d除以归一化后的模长 d并用上表实测值交叉校验非引用第 2.2 节BERT 句子嵌入诱导出非平滑的各向异性语义空间损害语义相似度任务表现On the Sentence Embeddings from Pre-trained Language ModelsBohan Li 等EMNLP 2020arXiv:2011.05864https://arxiv.org/abs/2011.05864 2026-10-19 直连摘要页核到原文第 3.1 节用权重共享、似然最大化训练生成模型时学到的词嵌入倾向于退化并分布到狭窄锥形区域限制表示能力representation degeneration problemRepresentation Degeneration Problem in Training Natural Language Generation ModelsJun Gao 等ICLR 2019arXiv:1907.12009https://arxiv.org/abs/1907.12009 2026-10-19 直连摘要页核到原文第 3.1 节Cosine 相似度以「归一化向量上的点积」实现且向量在上传时自动归一化CollectionsQdrant 官方文档https://qdrant.tech/documentation/manage-data/collections/ 2026-10-19 直连核到原文原 concepts/collections 路径已跳转至此第 6.3 节归一化向量上的点积等价于余弦cosine 会再归一化一次因此 dot 更快Semantic Textual SimilaritySentence Transformers 官方文档https://sbert.net/docs/sentence_transformer/usage/semantic_textual_similarity.html 2026-10-19 直连核到原文第 4.2 节把 query 与 corpus 都做 normalize_embeddings 后可用点积计算得分Semantic SearchSentence Transformers 官方文档https://sbert.net/examples/sentence_transformer/applications/semantic-search/README.html 2026-10-19 直连核到原文第 6.3 节任何具体嵌入模型的维度、归一化策略与真实分数分布待验证本文未核任何具体模型的官方说明故正文不写产品名、版本号与推荐阈值全文边界声明四步测阈值流程与两段统计脚本本文方法说明非引用第 4.2、6.2 节片段为示意写法成稿时未在本机复跑均已在围栏外标注「代码待验证」第 4.2、6.1、6.2 节附表 B术语速查表术语一句话解释在本文哪里用到向量一串有序的数用来代表一段文本第 1 章开头维度这串数一共有几个第 1、2 章嵌入Embedding把文本变成向量的那个模型或那一步第 1.2 节起余弦相似度两个向量夹角的余弦只看方向不看长度第 1 章、第 4 章点积两个向量对应位置相乘再求和同时受方向和模长影响第 4.1 节模长向量从原点出发的长度第 4.1 节归一化把向量长度缩到 1只保留方向第 4.2 节幂等同一个操作做一遍和做两遍结果一样归一化就属于这种第 6.3 节各向异性向量不是均匀铺满球面而是挤在一个窄锥里第 3.1 节共享偏移所有向量都带着同一个方向的分量把无关文本的分数一起抬高第 3.2 节表示退化训练中嵌入挤进窄锥、表示能力受限的现象第 3.1 节分布收窄分数集中在很窄的区间绝对阈值失去区分力第 2.3、7.1 节交叠区正样本的低分尾与负样本的高分尾重叠的那段区间第 6.1 节分位数阈值不设固定分数改为取分数最高的若干比例第 7.1 节漏判 / 误判该召回没召回 / 不相关被召回阈值的两端代价第 5.3 节写在最后这篇用到的资料写这篇文章时把相关的官方文档和源码又翻了一遍顺手也整理了几份配套的东西大模型学习路线图从零基础到能自己动手做 Agent按阶段说明每一步该学什么、哪些可以先跳过《LangChain LangGraph MCP 智能体开发实战》视频课7 个模块从私有化部署、EmbeddingRAG 到 MCPAgent 全流程AI 大模型知识库在线可查Agent Skills 从入门到落地、Claude Skills 完全指南等专题按目录浏览即可640 套 AI 大模型行业报告 经典 PDF 书籍看行业落地案例和别人怎么做的时候用得上大模型零基础到精通教学视频跟着敲一遍比只读文档快得多资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「AI」优先通过。资料按「先路线、再动手、最后查漏」的顺序整理好了建议先看学习路线那一份照着它挑一条适合自己当前基础的路径再往下看。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑