Skip to content

Latest commit

 

History

History
140 lines (84 loc) · 15 KB

3.文档内容处理.md

File metadata and controls

140 lines (84 loc) · 15 KB

3.文档内容处理

RAG在整个大模型技术栈里的重要性毋庸置疑,而在RAG中,除了大模型之外,另一个不可或缺的部分,就是搜索系统,大模型的正确、稳定、可控生成,离不开精准可靠的搜索系统,大量的实验中都有发现,在搜索系统足够准确的前提下,大模型的犯错情况会骤然下降,因此,更全面、系统地了解搜索系统将很重要。

本期介绍的是文档内容处理,这块主要聚焦在离线非推理阶段,俗话说,巧妇难为无米之炊,文档就是搜索系统的米,没有文档资源,搜索无从谈起,然而,面对各式各样的文档,往往需要进行各种处理,使之变成方便检索、方便展示的形态,才能为我们所用,本文主要就讲解,文档内容处理的核心工作以及常用的方法。

1.文档内容处理的流程

和原来讲RAG文档处理文章里写的相同(知识文档处理和使用流程),知识从原始材料到最终使用,主要会经历离线和在线两个阶段

  • 离线阶段是指,是把原始材料存入数据库的流程,这个阶段是离线处理的,所以被称为离线阶段;
  • 在现阶段是指通过query被检索出来并且使用的,这就是在线阶段。

离线阶段会经历这几个过程:

  • 基础文档解析:将各种形式的文档,转化可以容易处理模式,常见的是转为文本。
  • 内容理解:从文档中提取各种重要信息,可以是关键词、实体、标签、摘要、切片等显式信息,也可以是embedding这种隐式信息。
  • 入库:存到数据库中,做成方便检索的索引,供在线的搜索使用。

在线阶段会经历这几个过程:

  • 检索出库:被搜索出来,从数据库中被取出。
  • 排序、判别、过滤:对检索出来的知识进行进一步的筛选,选出最合适的备选知识。
  • 内容使用:对检索出来的结果进行利用。

可以看到,整个流程和RAG相比只有最后一步略有不同,其他的情况都非常类似。

在线阶段本期先不讲,后面的章节会逐步展开,本文主要讲离线阶段的工作。

2.内容解析

离线阶段的工作就是把原始材料接入内部搜索引擎,这里的关键难题在于原始材料的类型是多种多样的,一般通用RAG的原始材料的来源是用户自主上传的文档,所以很多开源项目的工作重心都放在各种类型文档的解析(RAG开源项目Qanything源码阅读2-离线文件处理),例如QAnything内就有大量针对pdf、doc、png等格式的处理,然而从搜索的角度看,输入的原始材料并不局限于此。

现在要说的,是另一种格式,那就是接口模式和HTML内容的接入。

接口模式大都来源于企业或者大团队之间的合作,在进行合作后,会有一些文档数据,会通过接口的方式接通,常见的就是通过http链接,通过批量的方式进行同步,举个例子:

import json,requests

def run_client_info(url, query):
    response = requests.post("{}/info".format(url), json={"query": query})
    return json.loads(response.text)

此处是通过http请求一个接口,并将返回的内容转化为json,在python里就是dict类型,内部的数据就已经完成了读取。

另一种就是HTML内容接入,这种一般是通过网络爬虫的方式接入的,时至今日有些搜索引擎的爬虫(前排提醒,爬虫需要注意版权等法律风险),这里的内容很大概率就是html的格式,例如这个是百度热搜下一个条目的内容:

<div class="toplist1-tr_1MWDu"><div class="toplist1-td_3E2-U opr-toplist1-link_2Ag1v"><span class="c-index-single toplist1-hot_1xI8N  c-color-red toplist1-hot-normal_3wyjO" style="opacity:1;"><i class="c-icon icon-top_1leHV"></i><!--13--></span><a target="_blank" title="照见传承的两棵榕树" href="/s?wd=%E7%85%A7%E8%A7%81%E4%BC%A0%E6%89%BF%E7%9A%84%E4%B8%A4%E6%A3%B5%E6%A6%95%E6%A0%91&amp;usm=4&amp;ie=utf-8&amp;rsv_pq=9fcc13a6003bab94&amp;oq=%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%20python&amp;rsv_t=bc08apZFnVPcuNWJDfalpiE6%2FR438LchYynixw4xpr8INGuCoZ3DQztg%2Bxo&amp;rqid=9fcc13a6003bab94&amp;rsf=a7c75473cc57caa39528e7bf5ff29a3f_1_15_1&amp;rsv_dl=0_right_fyb_pchot_20811&amp;sa=0_right_fyb_pchot_20811" class="c-font-medium c-color-t opr-toplist1-subtitle_1uZgw" data-click="{&quot;clk_info&quot;:&quot;{\&quot;index\&quot;:1,\&quot;page\&quot;:1,\&quot;city\&quot;:\&quot;\&quot;,\&quot;type\&quot;:\&quot;hot_fyb\&quot;}&quot;}">照见传承的两棵榕树<!--14--></a><!--15--></div></div>

一般我们关注的是两个内容,一个是href下的链接方便进一步爬取,另一个是具体的文字内容,即“照见传承的两棵榕树”这部分,常见的html解析方案,大家可以参考beautifulsouplxml

3.内容理解

内容理解,这次换个说法,内容理解的核心目的是为了提升在线搜索的匹配效果。我们原始获取、解析好的内容一般是混杂的,不规则的,而且很大概率和用户的搜索习惯并不匹配,直接使用很可能会搜不到,而如果对这些内容进行合适的处理,例如抽取一些关键词、关键标签,或者进行合理的切片、向量化,就能很大程度降低在线检索的压力,提升搜索的准确率和召回率,需要强调的是,在线搜索的准召,并不只在于在线流程里的query理解、召回和排序,还需要关注离线的内容理解,内容理解如果有足够的优化,能降低在线准召很大的压力。

这里讲两种最常见的内容理解手段,标签抽取向量化,并对这俩内部的含义进行详细的扩展,让大家更能理解这两个方法在实际应用中的地位,以及具体实操需要关注的细节。

3.1 标签抽取

标签抽取式内容理解中很常见的方案,为了更好地进行在线匹配,离线提前做好标签抽取,标签抽取可以配合query理解,让搜索快速达到高准确的效果

举个例子,对于音乐场景的搜索,用户的搜索习惯一般是输入歌手、歌名、专辑、风格、作曲人、作词人等,还有一些是歌词,这些内容多半有一个特点——标签化,用户输入“周杰伦”,那直接查“singer=‘周杰伦’”即可,而这里的前提就是,知识库内需要有这个字段,即结构化的信息,而有这个的前提是,需要从原有的内容里抽取。至于抽取的方法,在NLP领域这方面已经非常成熟,这就是一个NER(命名实体识别)或者是序列标注任务,当然如果有合适的词典,直接用词典匹配也可以轻松解决(我常说的词典匹配到底怎么做)。

这种针对性更强的思路相比于大家很常见的向量召回方案,有如下几个优点:

  • 准确率高。
  • 可解释性更强,可控性也比较高。
  • 因为直面用户搜索习惯,所以大盘效果和用户体验提升非常明显。

缺点也比较明显:

  • 离线工作增加,而且离线内容理解存在效果问题。
  • 定制化明显不通用。(但其实比较成熟的系统,有自己的NLP通用能力,可以把这种类似标签提取的任务通用化,也是可以的)

另外需要注意的是,标签抽取强依赖对用户习惯的洞察,要有目的地提取有效的标签,如果命中率低了,这点的意义肯定不会很大。

3.2 向量化

向量化应该是现在大家最容易想到(只能想到)的内容理解方法,这确实是一个比较通用的方案了,但实际上这里会有很多需要考量的因素,列举一些:

  • 向量化的依据是什么,内容文本语义、用户行为、领域方向、划分粒度等。不同的向量化依据会衍生出不同的向量和匹配模式。
  • 目前的内容是否适合向量,例如数字、地理位置这种,向量可能不是那么好的选择。
  • 即使适合,还有很多因素会影响向量的质量,例如长文本会稀释句子里的关键信息,从而无法被搜到或者总是被搜到。

可见,即使是向量化,背后的要考虑的内容还是会比较多的,现在仔细展开来讲。

向量化依据,即,要把一个文档或者某个内容做向量化,是需要有参考依据的,否则和拿随机数做向量并无区别。来展开说下向量化可能的依据以及内部要考虑的细节:

  • 语义的向量大家会比较熟悉,甚至现在已经有开源的模型可以直接使用。但相似的概念在具体业务视角可能并不相同,需要结合实际业务定义,可能要考虑做进一步的模型微调,当然包括QA匹配和QQ匹配等句子对是否对称的问题,需要根据这个合理选择或者拆解任务模型。
  • 用户行为的模型。类似推荐系统里,有些用户行为或者高频的query,可以根据用户的点击行为,找到更喜欢被点击的内容,这个思路和协同过滤是类似的。
  • 对于语义空间差距比较大的多个领域,模型统一处理的效果可能并不是很好,此时拆开是一个不错的选择,各自模型管各自的领域,对综合效果的提升也有收益。

第二个问题则是向量模型的缺陷,向量模型是有自己的优势和劣势的,并非所有方案都适合用向量,或者说向量模型并非万能,在使用过程中要警惕向量模型是否踩到了某些业务的痛点从而导致不可用。

  • 数字等比较特殊的数据类型,现在的模型普遍对数字不够敏感,数字效果较差,如果是对数字比较重视的业务场景,向量模型可能并不那么适合。
  • 相似实体较多的场景,像工业场景下的型号,差一个字可能差距很远的,化学、医学领域像氯化钠、氯化钾啥的,对向量模型而言会比较困难,相比之下不如考虑用标签提取的思路,可能会更加适合。

第三点则是经验之谈,有关向量的调优和问题的定位问题,例如长文本表征总会因为内容过多而被稀释或者忽略,此时相似度做起来就没那么容易,这个需要和切片之类的操作配合,实现更好的表征和检索。

最后强调一下,向量不仅为了通用,为了突破效果上限,真的很有必要精雕,通过任务拆细、微调的方式,对最终的召回质量提升会很明显,在系统搭建的后期会是重要的提升点。

3.3 其他操作

这里为了完整性,多提一些操作,为了方便检索提升召回率啥的,是可以进一步多去做一些挖掘工作的,这些活可能看起来比较脏,但做起来是真的有些收益,包括但不限于这些:

  • 切片尽量把内容缩短,或者直接对长句做个摘要转化(用大模型)再来入库。
  • 多挖掘一些同义词、同义说法,放一起检索有利于提升召回率。
  • 能结构化尽量结构化,包括各种库表数据,不要着急合并成纯文本来做。

4.内容入库/索引

入库本质是个偏工程的活,但在实际应用过程,算法会高频修改这个,例如新增挖掘的字段,更新向量检索方案之类的,所以我个人是非常推荐大家尽可能去了解,甚至把这活一定程度把握到自己的手里,会更方便自己把事推进下去。

然后,因为搜索引擎目前发展的已经比较完整完善,所以此处更多是给大家推荐多种技术方案,方便大家进行选择,同时也很建议大家能系统学习。

因为入库是个数据工程,不同的数据量应该采取不同的方案,此处按照3个等级来给大家介绍。

  • 如果是千条以下数据,其实并没有什么必要上特别的数据库和数据结构,只要模型不大,逐个匹配的性能已经可以足够支撑,除非目前已经成为了明显短板,否则不用升级,常规的排序模型面对的差不多也就是这个数据量。
  • 如果是几十万以内的数据,目前机器的内存仍然吃得下,那单机方案也是可以支持的,简单的可以自己写一个倒排索引,类似向量召回则可以考虑FAISS、ngt、hnswlib等工具直接随着服务来弄即可。
  • 如果数据比较多,或者机器内存压力比较大,则要考虑分布式的中间件来做了,比较常用的例如ElasticSearch、Milvus等,都可以支撑检索,当然选型的时候要注意看各种数据库支持的索引,是否支持自己预期的那种,别到时候用一半发现自己需要的功能中间件不支持会很尴尬。

在此,给大家推荐几个比较合适的工具,建议大家系统学学:

  • 单机的向量检索库,非常火的FAISS,然后hnswlibannoyngt都非常推荐大家了解下。
  • python的字面索引生态好像不是很好,只知道woosh,如果大家还知道别的欢迎评论区推荐。
  • 分布式的中间件工具ElasticSearch、Milvus比较常见,postgre、redis、mysql等工具有空也建议大家学习下。
  • 互联网场景,数据比较多的,大数据生态也要了解,关键词有hadoop、hdfs、hive、spark(python版有pyspark)。

5.小结

本文主要讲搜索场景下对原始文档内容、知识的多种处理方案,在讲解方案的同时也给出了可能面对的问题以及方案思考的注意点,希望对大家有所帮助。

另外再次补充,之前写过两篇非常相似的文章,有些内容并未在本文出现,例如某些实操的细节,RAG场景的特定操作等,有兴趣可以继续阅读。