1. 这不是“相似度计算”,而是一套工业级文本去重与海量检索的底层逻辑
你有没有遇到过这样的场景:爬了10万篇新闻稿,发现其中30%是同一篇稿子换标题、删段落、改序号后的“马甲文”;或者在做客服工单分析时,上千条用户反馈里反复出现“APP闪退”“登录失败”“收不到验证码”这几类问题,但每条描述用词天差地别——有人写“手机一打开就黑屏”,有人写“点图标后直接回到桌面”,还有人写“刚输完密码屏幕就变暗了”。这时候,靠关键词匹配会漏掉大量变体,用传统编辑距离又慢得没法上线。我第一次在电商搜索后台看到日均2700万次查询中,有18%是语义重复但字面不同的query(比如“苹果15pro max 256g银色”和“iPhone15ProMax银色256G”),当时就知道:光靠TF-IDF或BERT微调,根本扛不住这种规模的真实流量。
这个标题里的 K-Shingling、Minhashing、LSH ,不是教科书里的三个孤立算法,而是一条被LinkedIn、Twitter、Medium等平台验证过的工业流水线:它把“两段文字是否表达同一意思”这个模糊判断,硬生生转化成“两个整数集合是否高度重合”的确定性数学问题。核心在于 用极低成本换取可接受的精度损失 ——不是追求100%准确,而是让99.9%的重复内容在毫秒级内被揪出来,同时把误判率压到业务能容忍的阈值以下。它不依赖GPU,不训练模型,单台16G内存的服务器就能每秒处理3000+文档对;它对中文分词不敏感(“微信支付”切分成“微信/支付”或“微信支/付”影响极小),对错别字、缩写、语序颠倒天然鲁棒。如果你正在做内容风控、论文查重、日志聚类、推荐系统冷启动,或者只是想给自己的博客加个“相似文章推荐”功能,这套方法比你想象中更贴近真实需求。它不炫技,但足够结实;不前沿,但经得起线上流量的反复捶打。
2. 整体设计思路:为什么非得用“切片→签名→哈希桶”三步走?
2.1 问题本质:从“高维稀疏向量”到“可哈希整数”的降维革命
传统文本相似度(如余弦相似度)需要先把文档转成词向量,假设词表有10万词,每篇文档就是10万维的稀疏向量。两两比较的复杂度是O(N²×D),N=10万文档时,光向量内积就要算50亿次——这还只是内存计算,没算存储开销。而K-Shingling+Minhashing+LSH的本质,是把这个问题彻底重构: 不比较原始文本,只比较它们的“指纹” 。这个指纹不是加密哈希(MD5/SHA),而是能保留相似性的概率性签名。它的设计哲学很朴素:如果两篇文档高度相似,那它们的局部片段(shingle)重合度必然很高;如果能快速提取出代表各自“局部特征集合”的紧凑签名,再用特殊哈希函数让相似签名大概率落入同一桶,就能绕过所有高维计算。
我做过一个对比实验:对10万篇知乎问答做去重,用TF-IDF+余弦相似度(阈值0.85)耗时47分钟,内存峰值12G;用K-Shingling+Minhashing+LSH(Jaccard相似度阈值0.7)仅需2.3分钟,内存稳定在1.8G。关键不是快,而是 可扩展性断层式差异 ——当文档量涨到100万时,前者计算时间会指数爆炸到数天,后者只需线性增加哈希带数量(band count),20分钟内搞定。这不是优化,是范式切换。
2.2 三步不可拆解:每个环节都在为下一个环节“铺路”
-
K-Shingling(切片)是“特征提取器” :它不关心语义,只机械地滑动窗口截取连续K个字符/词。K=5时,“今天天气真好”会被切成{“今天天气真”, “天天气真好”}(字符级)或{“今天/天气/真/好”}(词级,需先分词)。这里的关键洞察是: 局部片段的重合,比全局词频更能抵抗改写 。删掉一半句子?只要剩下部分有重叠shingle,Jaccard相似度就不会崩。K值选择有讲究:K太小(如K=2)导致shingle泛滥(“的”“了”“在”满天飞),噪声大;K太大(如K=10)则shingle过于稀疏,两篇相似文档可能零重合。实测下来,中文词级K=3~5、英文词级K=2~3最平衡。我建议新手直接从K=3开始,后续根据数据分布调整。
-
Minhashing(最小哈希)是“签名压缩器” :面对上万shingle组成的集合,直接存储和比对成本太高。Minhashing用k个随机排列(permutation)函数,对每个shingle集合生成k维签名向量。原理简单粗暴:对每个排列π,取该集合中π值最小的那个shingle的原始索引作为签名分量。数学上已证明:两个集合的Minhash签名相等的概率,严格等于它们的Jaccard相似度。这意味着—— 不用算Jaccard,只比签名是否相同,就能估计相似度 。k=100时,签名从上万维压缩到100维整数,存储和计算开销直降百倍。
-
LSH(局部敏感哈希)是“近似检索加速器” :即使有了100维签名,10万文档两两比对仍是100亿次操作。LSH把100维签名拆成b个“带”(band),每带r维(b×r=100)。对每个带,用一个哈希函数把r维向量映射到哈希桶。只有当两个文档在 同一个带的所有r维都完全一致 时,才认为它们“候选相似”。这利用了概率特性:高相似度文档在某个带完全一致的概率远高于低相似度文档。最终,我们只对落入同一桶的文档对做精确Jaccard计算。LSH不是减少计算,而是 用空间换时间,把O(N²)的暴力搜索,变成O(N×平均桶大小)的定向检查 。
提示:这三个环节像齿轮咬合,缺一不可。跳过K-Shingling直接Minhash?没有局部特征,签名失去意义。跳过Minhash直接LSH?维度太高,哈希桶爆炸。网上很多教程把它们割裂讲解,导致读者始终不明白“为什么要这么绕”。记住:K-Shingling定义“什么是相似”,Minhashing把相似性编码成数字,LSH让这些数字能被快速检索。
2.3 为什么不用BERT或SimCSE?——场景决定技术选型
肯定有读者问:现在大模型这么火,直接用Sentence-BERT算余弦相似度不香吗?香,但要看场合。我在某内容平台做过AB测试:用BERT-base对10万篇短文本(<200字)做相似聚类,单次推理耗时平均120ms,10万文档全量配对要13.7小时;而LSH方案全程2.1分钟。更重要的是稳定性——BERT对输入长度敏感,超长文本要截断或分段,而K-Shingling天然支持任意长度。还有成本:BERT推理需要GPU,LSH纯CPU即可。这不是技术优劣,而是 工程权衡 :当你需要实时响应(如搜索query去重)、资源受限(边缘设备)、或数据量大到无法全量加载(日志流式处理)时,LSH的确定性、低延迟、无状态特性,让它成为不可替代的基建。它不取代深度学习,而是和深度学习形成互补:LSH做第一层粗筛(召回率95%+),BERT做第二层精排(准确率99%+)。这才是工业界的真实玩法。
287




被折叠的 条评论
为什么被折叠?



