最近在公司试水RAG系统,底层用的ChatGLM3-6B,检索这块换了几个开源embedding模型(BGE、m3e都试了),结果用户问“离职流程怎么走”,系统老是召回“考勤规则”或者“年假申请”的内容,感觉语义理解上差了点。我已经把文档切成了512 tokens的小块,也试过用标题加粗来加权,但还是经常答非所问。想问下各位大佬,中文场景下有没有更准的开源embedding模型?或者我是不是应该在预处理阶段加个意图分类?小白刚入坑,求指条明路。
用开源模型搭RAG,中文检索总跑偏,有啥好用的embedding推荐吗?
全部回复
共 155 条试试用text2vec-large-chinese,或者加个query改写模块,先判断意图再检索,能缓解不少。
我最近也在折腾类似的项目,BGE和m3e确实在中文语义上容易跑偏,尤其是人事这种高相似度的场景。可以试试text2vec-large-chinese,或者最近出的stella-base-zh-v3-5-1e-1,我换完之后召回准确率明显高了一截。另外你提到意图分类其实是个好思路,我加了简单的规则过滤后,像“离职”和“考勤”这种相近意图的误召减少了很多。文档切块的话,可以试试重叠滑动窗口,比纯硬切效果好很多。
你这情况我刚开始搞RAG时也遇到过,后来发现embedding模型对中文长尾语义确实容易翻车。可以试试stella-base-zh-v3-5-1e6或者UAE-Large-V1,这俩在中文问答对匹配上比BGE准不少,尤其是HR相关场景。另外预处理加意图分类挺有必要的,我这边用个轻量模型先分“流程咨询”“制度查询”再进不同索引,召回直接上了个台阶,切块的话512有点大,试试256配合重叠16,能改善边界语义丢失。
同款问题,之前我用bge-large-zh-v1.5也翻车过,后来换成stella-base-zh-v3-5-1.5b感觉语义匹配好了一点,但也没完全解决。你提到意图分类我觉得是条路子,可以先用个轻量分类器把“流程类”“政策类”分出来,再分别调不同的检索策略。另外文档切块前最好把层级标题和上下文保留,我试过把前后两段一起embedding,召回率能提几个点。
试试GTE或Stella,中文长文本检索比BGE稳不少,或者你可以在切块前加一层意图分类器把问题先分一下。
试试GTE-large或者Stella-base,中文语义理解会好一些;另外加个意图分类确实能减少误召回。
可以试试把chunk_size调到256以下,配合overlap做重叠切割,有些场景下对小粒度语义更友好。另外bge-m3官方其实有reranker模型,在召回后做二次排序能过滤掉不少噪声,我这边用了之后准确率提升挺明显的。意图分类确实可以考虑加上,但感觉对chunk质量和embedding本身要求更高,先优化这两个方向试试?
我也遇到过类似的问题,后来换了text2vec-large-chinese,感觉语义匹配比BGE和m3e稳一些,特别是短文本场景。不过你说切512 tokens,可能颗粒度还是偏大,有些关键信息被稀释了,我试过256甚至128,召回准确率反而上去了。另外加个意图分类确实有用,我一般先用规则筛一下常见问题类型,再根据不同意图选不同的检索策略,能减少不少误召回。
看到你这个问题太有共鸣了,我也是从BGE、m3e一路试过来的,中文场景下确实容易在“离职”和“考勤”这种强相关但不同意图的词上翻车。个人感觉单纯换embedding模型可能治标不治本,比如你要不要试试把文档按“流程类”和“规则类”先分开索引?像离职流程、休假流程这种带步骤的,和考勤规则、年假政策这种纯说明性的,语义空间本来就不太一样。另外,你提到的意图分类其实是个好思路,在query进来之前先判断是“流程查询”还是“政策查询”,再定向到对应的子索引,这样召回准确率能明显提升。还有一个细节,你的chunk size是512 tokens,对中文来说可能还是偏大,试试256甚至128,配合滑窗重叠,有时候能抓住更精准的片段。最后小声说一句,现在有些国产模型比如BCE-Embedding系列在中文任务上表现不错,你可以去ModelScope上扒拉一下看看。
同感,BGE和m3e在中文长尾语义上确实容易飘,尤其HR场景里“离职”和“考勤”这种强相关但不同义的概念。可以试试bge-large-zh-v1.5或者stella-base-zh,后者对短文本匹配更敏感。另外预处理加个prompt分类确实有用,我这边用ChatGLM3先判断用户意图是“流程查询”还是“规则查询”,再召回对应文档块,能减少不少误召回。你这512切块偏小了,试试1024加重叠窗口,上下文连贯性会好很多。
试试gte-Qwen2,7B版中文检索比BGE稳不少,另外预处理加个意图分类确实能缓解误召。
同感,中文RAG检索确实容易翻车,尤其HR场景下“离职”和“考勤”这种强相关概念很容易混淆。我之前试过把query扩写成多个相似问法再召回,效果比单次检索稳一些。另外可以试试用bce-embedding-base_v1,它在中文长文本和细粒度语义上比bge和m3e更准,而且支持动态权重。预处理加意图分类也是个好思路,但小团队维护成本可能偏高,建议先优化检索逻辑。
我最近也踩过类似的坑,后来换成了text2vec-large-chinese,感觉比BGE和m3e在垂直领域匹配上稳一些。不过512 tokens切法确实容易丢上下文,我试过把切块大小调到768,同时保留段落标题,召回率明显好了点。另外你提到的意图分类其实挺管用的,我在预处理阶段加了个简单分类器,把常见HR问题先分到对应模块,再进embedding检索,基本告别跑偏了。
试试加个意图分类确实管用,我这边用了bert-base-chinese做初筛,召回准了不少。
你这情况我也遇到过,BGE和m3e在中文长尾语义上确实有点飘。后来我们换了stella-base-zh-v3-5-1e6,配合把文档按语义段落切分(别死磕512 token),召回率明显稳了。预处理加意图分类也是好思路,不过可以先试试把query和文档标题做一次相似度加权,能缓解不少张冠李戴的问题。
试试加个query改写步骤,把用户问题转成更贴近文档表述的句式,比单纯换embedding见效快。
试试stella-base-zh或multilingual-e5-large,中文检索比BGE稳很多。
预处理加意图分类确实能救,但先换模型更省事。
试过m3e和BGE之后,最近在中文长文本上试了bge-large-zh-v1.5,感觉语义对齐好了不少,尤其对“离职流程”这类带动作的词能抓住重点了。不过512切块确实容易丢上下文,你可以试试把chunk size调到300-400,然后加个同义词扩展,比如“离职”关联“辞职”“离职手续”,召回率能上来不少。意图分类其实看你数据量够不够,如果文档主题比较杂,加一个简单分类器做路由确实能减少跑偏。
同款踩坑,BGE和m3e在中文长尾语义上确实容易飘。可以试试stella-base-zh-v3-5-1e8或者bge-large-zh-v1.5,后者在HR场景的垂直语料上微调过,召回率会稳一些。切块大小也可以调成256试试,512对短文本匹配可能太粗了。另外你提到意图分类的思路我觉得靠谱,至少在用户问流程类问题时能先过滤掉规则性内容,减少干扰。
说到这个我可太有同感了,BGE和m3e在中文长尾词和近义场景下确实容易翻车。我建议你可以试试bge-large-zh-v1.5或者stella-base-zh-v3-5-1e,这两个在领域内的小样本测试里表现比m3e稳不少。另外你说文档切512 tokens,会不会颗粒度还是太粗了?我经验里如果切到256甚至128,配合滑动窗口,召回率会有明显提升,尤其是“离职流程”这种带流程步骤的文档,小段落更容易匹配到精确片段。还有一个坑是,你用的ChatGLM3-6B做生成时,它的tokenizer对中文短句的语义密度理解有限,可以试试在检索后加一层reranker,比如bge-reranker-v2-m3,能把排序拉回来不少。至于意图分类,我倒是觉得可以加,但别用它做硬过滤,而是做个软权重,比如把用户输入先过一遍fasttext分到“人事流程”类,检索时给这类文档加0.3的分数加成,比单纯加权标题更有效。你目前文档里是不是有很多表格或者列表格式的数据?那些切分后语义会断掉,最好单独用layout-aware切分器处理一下。