来自工业界的知识库 RAG(六),独特的 RAG 框架 dsRAG 核心亮点解读

news2024/11/16 12:58:11

背景介绍

在前面介绍了较多的开源 RAG 框架,比如主打 Rerank 的 QAnything, 主打精细文件解析的 RagFlow, 主打模块化灵活组合的 GoMate。这些库的设计除了少量的独特之处外,相似的部分很多。

最近有注意到一款另类的 RAG 框架 dsRAG,使用了较多独特的 RAG 优化方案,因此花了一些时间对其核心亮点进行了考察,整理相关内容在这边。

核心亮点

官方对 dsRAG 的介绍是一款高性能非结构化数据检索引擎,从 2024 年 4 月开源至现在,Github star 数量为 730。相对其他 RAG 项目动辄几千 star 的量级,dsRAG 属于比较冷门的了。

从实际了解的情况来看,这个现状也比较正常,dsRAG 确实不太适合直接用在生产环境。但是不可否认 dsRAG 的独特之处确实值得了解下:

  1. 语义分块(Semantic sectioning):相对其他框架大多采取 Level 2 级别的递归字符切片,部分实现的是 Level 3 级别的基于文件类型的切片方案,而 dsRAG 实现的是 Level 4 级别的语义分块,有兴趣了解切片的等级可以查看 5_Levels_Of_Text_Splitting ;
  2. 自动上下文(AutoContext):这个是指在分块的内容前增加额外的上下文信息,这样可以提升单个块的内容的可检索性。目前大部分 RAG 框架都没有实现对应的能力;
  3. 文本块智能组合(Relevant Segment Extraction):为了避免分片信息不完整的问题,dsRAG 会将检索的文本块智能组合,这样在面对复杂问题时可以提供更完整的信息。目前大部分的 RAG 框架没有实现类似的功能;

下面就主要围绕上面提到的亮点,从原理出发,深入到实现层面,理解其中的技术细节。

功能细节

语义分块

语义分块是指基于语义的相似性切分文本,langchain 中就实现了类似的能力,langchain 会将原始文本切分为句子,之后比较句子向量的相似性,如果相似性超过一定的阈值,则进行切分。这样可以一定程度避免固定块切分带来的语义不完整的问题。

而 dsRAG 的语义分块是基于大模型实现,主要的流程如下所示:

  1. 将原始的文本按照换行符 \n 切分为按行的句子;
  2. 利用大模型生成分片方案,考虑到大模型的语义理解能力,大模型给出的分片可能比常规的向量模型更准确;

实际实现时基于大模型的结构化输出能力,因此目前只支持 OpenAI 和 Anthropic 的大模型:

system_prompt = """
Read the document below and extract a StructuredDocument object from it where each section of the document is centered around a single concept/topic. Whenever possible, your sections (and section titles) should match up with the natural sections of the document (i.e. Introduction, Conclusion, References, etc.). Sections can vary in length, but should generally be anywhere from a few paragraphs to a few pages long.
Each line of the document is marked with its line number in square brackets (e.g. [1], [2], [3], etc). Use the line numbers to indicate section start and end.
The start and end line numbers will be treated as inclusive. For example, if the first line of a section is line 5 and the last line is line 10, the start_index should be 5 and the end_index should be 10.
The first section must start at the first line number of the document ({start_line} in this case), and the last section must end at the last line of the document ({end_line} in this case). The sections MUST be non-overlapping and cover the entire document. In other words, they must form a partition of the document.
Section titles should be descriptive enough such that a person who is just skimming over the section titles and not actually reading the document can get a clear idea of what each section is about.
Note: the document provided to you may just be an excerpt from a larger document, rather than a complete document. Therefore, you can't always assume, for example, that the first line of the document is the beginning of the Introduction section and the last line is the end of the Conclusion section (if those section are even present).
"""

class Section(BaseModel):
    title: str = Field(description="main topic of this section of the document (very descriptive)")
    start_index: int = Field(description="line number where the section begins (inclusive)")
    end_index: int = Field(description="line number where the section ends (inclusive)")


class StructuredDocument(BaseModel):
    """obtains meaningful sections, each centered around a single concept/topic"""
    sections: List[Section] = Field(description="a list of sections of the document")

# 大模型输出结构化内容

client.chat.completions.create(
    model=model,
    response_model=StructuredDocument,
    max_tokens=4000,
    temperature=0.0,
    messages=[
        {
            "role": "system",
            "content": system_prompt.format(start_line=start_line, end_line=end_line),
        },
        {
            "role": "user",
            "content": document_with_line_numbers,
        },
    ],
)

可以看到就是一个简单的 ChatGPT 调用,就根据文本生成结构化的结果。后续根据分片的 start_indexend_index 确定分片的开始和结尾内容,从而实现了语义分片。

自动上下文

自动上下文主要期望解决原始文本不容易检索的问题,实际在生产环境应用过 RAG 服务很容易发现,原始文档中总是会存在部分内容不太容易检索。

比如以一个疾病的论文为例,可能会存在一段介绍疾病对应的治疗方案,但是治疗方案的内容中可能没有相关疾病的描述,如果检索相关病例的治疗手段,可能会发现无法在大量的文本中精准命中对应的治疗手段。

dsRAG 补充的上下文信息目前支持文档标题,文档摘要,文档块的标题,文档块的摘要。而这些内容从何而来呢?答案与上面的分片一样:大模型。

dsRAG 为上面的四种类型的数据分别实现一个对应的 prompt,之后就可以借助大模型实现对应的功能了,举例来看,实现文档标题的对应的 prompt 如下所示:

DOCUMENT_TITLE_PROMPT = """
INSTRUCTIONS
What is the title of the following document?

Your response MUST be the title of the document, and nothing else. DO NOT respond with anything else.

{document_title_guidance}

{truncation_message}

DOCUMENT
{document_text}
""".strip()

具体的生成过程就是一个简单的大模型调用,这部分就不展示具体的实现了。

生成对应的上下文数据之后,将上下文与分片的内容组合在一起进行了向量化,这样就得到了信息更丰富,更容易检索的文本块了。

文本块智能组合

文本智能组合(Relevant Segment Extraction, RSE)是将检索到的文本块进行聚类,并进行智能组合形成更长的文本。通过使用 RSE 方案,dsRAG 在回答复杂问题,特别是需要跨越多个文本的问题时就具备明显优势。

根据官方给出的测试数据来看,RSE 是对效果影响最大的策略,官方测试评分如下所示:

Top-kRSECCH+Top-kCCH+RSE
AI Papers4.57.94.77.9
BVP Cloud2.64.46.37.8
Sourcegraph5.76.65.89.4
Supreme Court Opinions6.18.07.48.5
Average4.726.736.048.42

可以看到,通过单纯的 RSE 策略就可以取得不错的分数,当然自动上下文(CCH) + 文本块智能组合(RSE) 的组合肯定能取得更高的分数。

RSE 实现的效果如下所示:

请添加图片描述

在上面的图中,dsRAG 向量检索命中了 chunk1, chunk3, chunk4。如果不使用任何优化手段,大模型拿到的是独立的三个分片,信息都是碎片化的。遇到需要综合这些文本块的问题时,往往效果不佳。

通过 RSE 机制,实际可能组合生成的是一个包含 chunk1, chunk2, chunk3, chunk4 完整片段,从而可以提供更完整的信息用于问题回答,大模型返回的答案预期也会更好。

RSE 主要用于对检索后的文本进行组合,其中包含的步骤如下所示:

  1. 计算文件中检索命中的第一块到最后一块文本的相关性评分,注意没有被检索到的文本块可能也需要评分。比如文本中包含 chunk1 ~ chunk10 的文本块,但是检索时命中了 chunk3, chunk4, chunk7, 那么就需要计算 chunk3 ~ chunk7 的文本块的相关性评分;
  2. 计算文件中所有可能的连续组合文本块的评分,选择最高的评分的组合文本块进行使用;
评分计算

文本块的评分是基于文本块的排名,相似分计算出来,并使用文本长度进行缩放,计算的公式如下所示:

请添加图片描述

其中的变量如下所示:

  • rank: 为检索排名,如果没有检索到,那么排名为 1000;
  • relevance_value: 为检索相关性分数,没有检索到的情况下,分数为 0
  • chunk_length: 为文本长度,可以看到文本长度越大,评分是线性增加的;
  • delay_rate: 衰减率常量,可以决定排名的相对重要性;
  • chunk_penalty: 无关块的惩罚常量,作为一个阈值调整获得长文本块的倾向性,当值为 0.05 时,一般会生成 20~50 个的长文本块,当值为 0.4 时,一般给出会给出 1~3 个文本块的短文本块。

可以看到检索排名 rank 会对最终结果产生指数级的差异,检索相似分 relevance_value 会对结果产生线性差异,而最终的评分会被文本长度 chunk_length 进行线性缩放。

其实现如下所示:

# 计算原始评分

def get_chunk_value(chunk_info: dict, irrelevant_chunk_penalty: float, decay_rate: int):
    rank = chunk_info.get('rank', 1000)
    absolute_relevance_value = chunk_info.get('absolute_relevance_value', 0.0)
    v = np.exp(-rank / decay_rate)*absolute_relevance_value - irrelevant_chunk_penalty
    return v

# 基于文本长度进行评分缩放

def adjust_relevance_values_for_chunk_length(relevance_values: list[float], chunk_lengths: list[int], reference_length: int = 700):
    adjusted_relevance_values = []
    for relevance_value, chunk_length in zip(relevance_values, chunk_lengths):
        adjusted_relevance_values.append(relevance_value * (chunk_length / reference_length))
    return adjusted_relevance_values

组合确定

根据上面计算的各个文本块的评分,需要确定各个连续分块组合的最大值,熟悉算法的同学应该都想到经典的滑窗算法,但是 dsRAG 实现的是暴力求和,具体如下所示:

for start in range(len(relevance_values)):
    # 跳过开始为 0 情况

    if relevance_values[start] < 0:
        continue

    for end in range(start+1, min(start+max_length+1, len(relevance_values)+1)):
        # 计算连续块的得分,更新可能的最大得分

        segment_value = sum(relevance_values[start:end])
        if segment_value > best_value:
            best_value = segment_value
            best_segment = (start, end)

通过上面的实现可以容易理解,RSE 会尽可能扩张与问题相关的文本块。即使当前块没有被检索到,如果前后临近文本块都被检索到,那么同样有可能会被加入组合中。在实际的文本中,如果前后的文本块都与问题相关,那么当前块大概率也是与问题相关的。

熟悉滑动窗口的都知道,滑动窗口算法会尽可能扩张大于 0 的窗口值,这样就能理解为什么上面计算公式中的 chunk_penalty 可以直接快速影响最终组合块的长度了。

总结

dsRAG 是一个比较有意思的 RAG 库,提供了一些不同常规的 RAG 优化思路,在较多的地方大幅高频使用大模型可能会导致生产环境使用的成本过高,但是不妨碍其中的思路值得借鉴,特别是 RSE 机制,预期可以对 RAG 的效果有明显的改善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2085984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openGauss在龙芯平台部署的实践

服务器环境 系统信息 NAME"Loongnix-Server Linux" VERSION"8" ID"loongnix-server" ID_LIKE"rhel fedora centos" VERSION_ID"8" PLATFORM_ID"platform:lns8" PRETTY_NAME"Loongnix-Server Linux 8"…

第四十篇-TeslaP40+Ollama+Ollama-WebUI(自编译)

本文介绍用自己编辑ollama-webui,链接本地ollama 环境 系统&#xff1a;CentOS-7 CPU: 14C28T 内存&#xff1a;32G 显卡&#xff1a;Tesla P40 24G 驱动: 535 CUDA: 12.2 Ollama: 0.3.0本地ollama 参考 [第二十四篇-Ollama-在线安装](https://blog.csdn.net/hai4321/articl…

2024软考:一场与“难”共舞的奇妙冒险,你值得拥有!

在这个时代&#xff0c;如果说有什么考试能让IT界的勇士们闻风丧胆&#xff0c;又爱又恨&#xff0c;那软考绝对能C位出道&#xff0c;成为众多技术大佬心中的“白月光”与“朱砂痣”。随着岁月悠悠&#xff0c;2024年的软考似乎又悄悄地在难度上动了点小心思&#xff0c;让人不…

vue设置水印

水印图例 1.新建Watermark.js 文件 const watermark {}const setWatermark (text, sourceBody) > {const id Math.random() * 10000 - Math.random() * 10000 / Math.random() * 10000if (document.getElementById(id) ! null) {document.body.removeChild(document.getE…

阿里云服务器开放端口的完整版图文教程

原文&#xff1a;阿里云服务器开放端口完整版教程&#xff1a;https://www.yundashi168.com/488.html 笔者近期开发完成的服务端程序部署在阿里云的ECS云服务器上面&#xff0c;一些应用程序配置文件需要设置监听的端口&#xff08;如Tomcat的8080、443端口等&#xff09;&…

萤石云 移动端demo指南

再来一篇&#xff0c;这次是萤石开放平台移动端demo的使用指南 一、Demo使用指南 从官网下载demo&#xff0c;下载地址demo首页如下&#xff1a; 填入对应参数 输入框是否必填解释服务器区域是国内选择Asia-China&#xff0c;海外选择对应的区域。选择后ApiUrl和WebUrl会自动…

餐饮行业eHR人力资源管理系统应该如何选择?

数字化转型与增长成为餐饮企业品牌竞争的创新壁垒&#xff0c;越来越多的餐饮企业&#xff08;门店&#xff09;依托数字化工具和手段&#xff0c;覆盖从内部组织到外部的数字化升级&#xff0c;包括员工管理、营销、客户管理&#xff0c;以及采购供应链等各环节的数字化运营。…

Netty从入门到超神-NIO 三大核心(selector,channel,buffer)(二)

前言 上一篇文章认识了一下Java的三大IO&#xff0c;这一章节我们详细了解一下NIO的工作原理以及三大核心Selector,Channel,Buffer并尝试来做一些小案例。 Java NIO 模型 Java NIO有三个核心的组件&#xff1a; selector 选择器 &#xff0c; channel 通道 &#xff0c; buf…

SpringBoot日常:Spring之@PostConstruct解析

简介 spring的Bean在创建的时候会进行初始化&#xff0c;而初始化过程会解析出PostConstruct注解的方法&#xff0c;并反射调用该方法。 PostConstruct 的使用和特点 只有一个非静态方法能使用此注解&#xff1b;被注解的方法不得有任何参数&#xff1b;被注解的方法返回值必…

Marin说PCB之TP测试的Layout设计要求

提及到TP点这个器件想必诸位道友们肯定不会陌生吧&#xff0c;我们的单板在量产之前都是需要做很多测试的&#xff0c;一般在产品研发的A版本和B版本的时候都是需要在单板上加上这个器件的。小编我最近在做一个改板&#xff0c;项目组为了降本增效&#xff0c;把单板的尺寸缩小…

Git 忽略已经提交的文件

对于未提交过的文件直接用ignore文件即可,不再赘述 对于已经提交过的文件,但是实际上不需要的,可以用git rm --cached命令 比如下图这个 .vsconfig被我误提交了或者忘了在ignore里添加了 但是我实际上不想要这个文件,那么在项目根目录打开git bash ,输入 git rm --cached .vsc…

LMDeploy 量化部署

创建环境和模型 conda create -n lmdeploy python3.10 -y conda activate lmdeploy conda install pytorch2.1.2 torchvision0.16.2 torchaudio2.1.2 pytorch-cuda12.1 -c pytorch -c nvidia -y pip install timm1.0.8 openai1.40.3 lmdeploy[all]0.5.3 mkdir /root/models …

[海思3403] 初始配置

虚拟机和板卡桥接 首先将虚拟机设置为桥接模式 板卡用网线和PC机连接&#xff0c;PC机用VMware打开Ubuntu虚拟机 点击虚拟网络编辑器&#xff0c;点击更改设置

爬取数据时,如何避免违法问题

目录 如何判断一个网站是否有明确禁止爬取数据&#xff1f; 如何处理爬取到的个人隐私数据以符合数据保护法规&#xff1f; 在爬取数据时&#xff0c;如何避免给目标网站带来过多的流量压力&#xff1f; 思维导图 在爬取数据时&#xff0c;避免违法问题的关键在于确保遵守相…

智慧工地:物联网技术和传感器技术的应用

随着科技的不断发展&#xff0c;物联网技术在各个领域得到了广泛的应用。在建筑行业中&#xff0c;智慧工地系统中物联网应用正逐渐成为一种趋势。本文将深入探讨智慧工地系统中物联网和传感器技术应用的内容。 物联网&#xff08;IoT&#xff09;和传感器技术在智慧工地中扮演…

养宠家庭除浮毛必入!希喂、安德迈、有哈宠物空气净化器真实对比

养过猫咪的铲屎官应该都体验过被换毛季支配的恐惧吧&#xff0c;夏天布偶的掉毛量已经全新升级了&#xff01;不仅是物体表面&#xff0c;连空气中都夹杂着浮毛&#xff0c;早上起来鼻子里偶尔都能发现它们的身影。长期生活在这样的环境中&#xff0c;肯定会对身体健康造成损害…

Spring数据访问层管理 ▎集成MyBatis ▎AOP ▎事务管理 ▎SpringWeb配置

前言: 在现代软件开发中&#xff0c;数据访问层的管理至关重要。Spring框架凭借其模块化结构和易用性&#xff0c;成为Java EE开发的首选。本文将探讨Spring在数据访问层的管理、MyBatis的集成、面向切面编程&#xff08;AOP&#xff09;、事务管理和Spring Web配置。 数据访…

【Material-UI】Select组件中的Native Select与TextField详解

文章目录 一、Select 组件概述1. 组件介绍2. Native Select 与 TextField 的区别 二、Native Select 组件详解1. 何为 Native Select2. Native Select 的基本用法3. Native Select 的优势与适用场景4. 自定义 Native Select 的样式 三、TextField 与 Select 的结合使用1. TextF…

BMS(三)

BAT (Pin 1): 连接到电池组的最高电压端&#xff08;CELL5&#xff09;。芯片通过该引脚监控整个电池组的总电压。 VC5 (Pin 2): 连接到第五节电池的正极&#xff0c;用于监控第五节电池的电压。 VC4 (Pin 3): 连接到第四节电池的正极&#xff0c;用于监控第四节电池的电压。 V…

【Datawhale X 李宏毅苹果书 AI夏令营】《深度学习详解》Task2 打卡

文章目录 前言学习目标一、线性模型二、分段线性曲线总结 前言 本文是【Datawhale X 李宏毅苹果书 AI夏令营】的Task2学习笔记打卡。 学习目标 李宏毅老师对应视频课程&#xff1a;https://www.bilibili.com/video/BV1JA411c7VT?p3 《深度学习详解》第一章主要介绍了深度学习…