
把 3072 维截成 256 维第一轮只取 100 个候选再加载全维重排这条链路很容易讲清也很容易被误解成“既省内存又绝对无损”。关键限制是全维重排只看得到第一轮留下来的文档。Matryoshka Representation Learning 让表示在训练时适配不同粒度。它不是说任意一个向量模型随便截掉后半段都保留同样的语义。MRL 原论文 的研究结果有自己的模型与任务条件不宜直接替换成私有知识库的性能表。两份成本账不能只记低维索引低维索引常驻内存全维向量放在磁盘或另一层存储这样才有机会减少内存驻留。若实现里仍把全库完整向量存在 Python 字典中就没有演示存储分层。磁盘点读还会引入 I/O、缓存与并发控制成本不是“全维点积很短所以端到端免费”。评测时保留三份结果全维精确搜索作为参考、低维候选池、候选池内全维重排。第一份与第三份的差异可以帮助区分候选遗漏和重排计算的问题。再单独比较 ANN 与低维精确搜索避免把近似索引误差也归因于维度截断。一个只有两份文档的反例A 与 B 在第一维相同低维排序只能按稳定规则打破平局。如果候选池只有一个名额可能恰好留下 A完整向量明明能认出 B却没有机会看到它。例子的数字是手工构造目的是证明限制存在不是衡量发生频率。内图的全维存储与低维驻留是两层部署概念本地代码为了展示排序反例使用小字典不能据此声称已经节省真实内存。import math def unit(v): norm math.sqrt(sum(x*x for x in v)) if norm 0: raise ValueError(zero vector) return [x/norm for x in v] def dot(a, b): if len(a) ! len(b): raise ValueError(dimension mismatch) return sum(x*y for x, y in zip(a, b)) vectors {A: [0.8, 0.6], B: [0.8, -0.6]} query [0.8, -0.6] def cascade(candidate_k): if not 1 candidate_k len(vectors): raise ValueError(invalid candidate depth) q_low unit(query[:1]) candidates sorted(vectors, keylambda d: ( -dot(unit(vectors[d][:1]), q_low), d))[:candidate_k] return sorted(candidates, keylambda d: (-dot(unit(vectors[d]), unit(query)), d)) assert cascade(1) [A] assert cascade(2)[0] B assert dot(unit(vectors[B]), unit(query)) dot(unit(vectors[A]), unit(query)) print(candidate_k1 misses B; candidate_k2 reranks B first)例子跑通以后还要验证什么这段代码已在本地 macOS、CPython 3.9.6 下运行实际输出为candidate_k1 misses B; candidate_k2 reranks B first。它验证的是例子中的数据约束与分支不包含线上服务的吞吐或延迟测量。没有使用经过 MRL 训练的模型、真实磁盘向量存储或 ANN。上线方案要记录模型支持的维度、截断后归一化方式、候选保真率、全维读取字节数和冷缓存尾延迟。若目标只是减少向量字段体积也不能把索引拓扑和服务进程的所有内存等比例减掉。延伸阅读Matryoshka Representation Learning2022 年提出的表示学习方法本文未复现论文训练。Universal I/O in Qdrant2026 年 10 月 12 日对本地与远程读取成本差异的工程说明不引用其延迟为本文测量。