报错是这么冒出来的。为了省内存把 HNSW 索引里的向量从 float32 改成 float16 存储,索引体积降了四成,但同一批查询的召回从 0.92 掉到 0.88,团队在评估这点召回损失换内存是否划算。

中间走过弯路,把「只有存储降精度,查询向量仍是 float32」当成主因,改完现象没变。

排查方向其实有限,先把可能性排一排。float16 精度不足,近邻之间的距离差被抹平。另一种情况是余弦相似度在低精度下排序不稳定。

不容易想到的是,阈值按 float32 的分布定的,没有重新校准。

先在同一批查询上对比两种精度

python eval_recall.py --index idx_fp32 --index2 idx_fp16 --cases queries.jsonl --k 10

接着把查询向量也转成同精度再测

python -c "import numpy as np;v=np.load('q.npy').astype(np.float16);print(v.dtype, v.nbytes)"

再往下按新分布重标相似度阈值

python tune_threshold.py --scores fp16_scores.jsonl --target-precision 0.9

压测内存与延迟的收益

python bench_index.py --index idx_fp16 --qps 200 --report mem,p99

怎么算真的好了,看的是同一批现象。查询向量与索引精度一致后召回回到 0.91,内存省下四成,P99 延迟没有变差,阈值重标后误召下降。

这里还有个坑。降精度省的是内存,赔的是排序稳定性,查询侧精度必须和索引对齐,阈值也要跟着重新校准。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。

作者 9823189