多模态RAG赛题实战之策略优化--Datawhale AI夏令营

科大讯飞AI大赛(多模态RAG方向) - Datawhale

项目流程图

1、升级数据解析方案:从 fitz 到 MinerU

PyMuPDF(fitz)是基于规则的方式提取pdf里面的数据;MinerU是基于深度学习模型通过把PDF内的页面看成是图片进行各种检测,识别的方式提取。

(1)识别表格 :将表格转化为结构化的Markdown或JSON格式。

(2)提取图片 :对文档中的图片进行识别。

(3)图片描述 :(可选)调用多模态模型为提取出的图片生成文字说明。

这会为后续的RAG流程提供包含表格和图片信息的、更丰富且更精确的上下文,是解决多模态问题的关键举措。

  • 基础方案所使用的 fitz 工具仅能提取文本,会遗漏表格、图片等关键信息。

  • MinerU 的优势:对PDF进行深度的版面分析,除了能更精准地提取文本块外,还具备以下功能:

  • 因此转而使用 mineru_pipeline_all.py 脚本,具体操作如下:
​# 建议在GPU环境下运行,执行完大约需要1.5h
python mineru_pipeline_all.py

报错:系统网络无法访问外网 huggingface.co,导致 Mineru 无法下载所需的 PDF 处理模型。

解决方法:在 mineru_pipeline_all.py 文件开头添加环境变量设置:

os.environ['MINERU_MODEL_SOURCE'] = "modelscope"

这将使 Mineru 从 ModelScope(阿里云模型库)下载模型,避免因网络问题无法访问 Hugging Face,下载到的是OpenDataLab的MinerU2.0-2505-0.9B模型。

mineru_pipeline_all.py 文件中关键的有两个函数依次执行:

1、parse_all_pdfs 函数——分析 PDF 的版面布局,识别出里面的文本、标题、表格和图片,然后把这些识别出的所有内容元素,连同它们的类型、位置、层级等信息,都存进一个名为 _content_list.json 的文件里。

2、process_all_pdfs_to_page_json 函数——读取 _content_list.json ,先按页码把内容分好组,然后逐个处理每一页里的内容项,里面内嵌了一个item_to_markdown 函数,这个函数是一个转换器,它根据内容项的类型( text、table、image)来决定如何转换成MarkDown格式,而且代码会检查图片本身有没有自带的文字描述( caption ),如果没有,并且我们允许进行视觉分析( enable_image_caption=True ),它就会调用一个多模态大模型(代码里指定的是 Qwen/Qwen2.5-VL-32B-Instruct来给这张图片生成一段描述。

MinerU的输出:会是三种类型——text、table、image的集合(相比于fitz则完全是text,对图表也是提取text,完全丢失了图的表意)

    {"type": "text","text": "分析师: 彭波  \nE-mail: pengbo@yongxingsec.com  \nSAC编号: S1760524100001  \n分析师: 陈灿  \nE-mail: chencan2@yongxingsec.com  \nSAC编号: S1760525010002  \n相关报告:  \n《伏美替尼持续放量,适应症拓展仍  \n有空间》2025 年 05 月 06 日","page_idx": 0},{"type": "table","img_path": "images/e615166fd385400608ed9069eb20088bb78ce2c5de7fad66da7072570597386f.jpg","table_caption": [],"table_footnote": [],"table_body": "<table><tr><td colspan=\"2\">基本数据</td></tr><tr><td>07月04日收盘价(元)</td><td>94.61</td></tr><tr><td>12mthA股价格区间(元)</td><td>39.82-99.99</td></tr><tr><td>总股本(百万股)</td><td>450.00</td></tr><tr><td>无限售A股/总股本</td><td>100.00%</td></tr><tr><td>流通市值 (亿元)</td><td>425.75</td></tr></table>","page_idx": 0},{"type": "image","img_path": "images/cefb4046fc651be250334d71e02a2c9289c2dc5420d575183f79eb329cdb68d5.jpg","image_caption": ["最近一年股票与沪深 300比较","资料来源:Wind,甬兴证券研究所"],"image_footnote": [],"page_idx": 0},

table:

image:原图和提取的图

但是目前mineru只是提取出来了图片,还需要对图片进行进一步的融合,只是简单加入图片的描述信息还有有比较大的局限性的。

2、升级分块策略

目前的分块策略:

按页来分块(每一页都是一个知识块,可以直接用于后续的向量化和索引),每一个pdf文件按页来排序,每一页的内容包含text、table、image,上一个pdf最后一页结束之后,便是下一个pdf的第一页,以此类推直到最后一个pdf的最后一页。

上述分块方式存在缺点:按“页”分块过于粗暴,一个完整的表格或逻辑段落可能被硬生生切开,或者说当本来应检索的信息分布于前后两页之中时,便破坏了信息的上下文完整性。

优化分块策略:

有了 MinerU 精细化的解析结果,我们可以对图片进行进一步的内容解释,添加图片的描述信息。

后续涉及对图像描述信息的融合处理。

3、引入重排模型

在终端下载BAAI的bge-reranker-v2-m3重排模型:

# 先下载 lfs
git lfs install
git clone https://www.modelscope.cn/BAAI/bge-reranker-v2-m3.git

加载重排模型:

# 初始化 FlagReranker(加载一次就行)
local_model_path = "./bge-reranker-v2-m3"  # 替换为你的下载模型路径
self.reranker = FlagReranker(local_model_path,use_fp16=True # 没 GPU 用 "cpu"
)

召回+重排实现代码:取的是先召回后重排得到的Top-k个chunks,代替原来的直接取的Top-k个chunks。

        # 1️⃣ 向量粗召回 15 个q_emb = self.embedding_model.embed_text(question)retrieved_chunks = self.vector_store.search(q_emb, top_k=15)if not retrieved_chunks:return {"question": question,"answer": "","filename": "","page": "","retrieval_chunks": []}# 2️⃣ 用 FlagReranker 精排pairs = [[question, chunk['content']] for chunk in retrieved_chunks]scores = self.reranker.compute_score(pairs)  # 返回每个pair的相关性分数# 绑定分数for i, sc in enumerate(scores):retrieved_chunks[i]['score'] = sc# 按分数排序,取 top_kreranked_chunks = sorted(retrieved_chunks, key=lambda x: x['score'], reverse=True)[:top_k]# 3️⃣ 拼接上下文context = "\n".join([f"[文件名]{c['metadata']['file_name']} [页码]{c['metadata']['page']}\n{c['content']}"for c in reranked_chunks])# 4️⃣ 构造 Promptprompt = (f"你是一名专业的金融分析助手,请根据以下检索到的内容回答用户问题。\n"f"请严格按照如下JSON格式输出:\n"f'{{"answer": "你的简洁回答", "filename": "来源文件名", "page": "来源页码"}}'"\n"f"检索内容:\n{context}\n\n问题:{question}\n"f"请确保输出内容为合法JSON字符串,不要输出多余内容。")# 5️⃣ 调用大模型client = OpenAI(api_key=qwen_api_key, base_url=qwen_base_url)completion = client.chat.completions.create(model=qwen_model,messages=[{"role": "system", "content": "你是一名专业的金融分析助手。"},{"role": "user", "content": prompt}],temperature=0.2,max_tokens=1024)

4、升级索引策略 

多路召回与融合:

除了原先的基于向量的语义检索——使用 embedding 模型来查找意思相近的chunk之外,另外再引入一种基于关键词的检索方法——BM25 算法,它擅长匹配问题中出现的具体词语,即要将chunk中的content内容的每一个单词/字给分出来,再去做匹配。

step1:下载BM25算法库和中文分词器

uv pip install rank_bm25, jieba

step2:在SimpleVectorStore类中新增 BM25 算法关键词检索函数,利用中文分词器(jieba)去对中文句子进行分词

class SimpleVectorStore:def __init__(self):self.embeddings = []self.chunks = []# --- 新增 ---self.bm25 = None  # BM25 模型self.tokenized_chunks = []  # 预先分好词的文本def add_chunks(self, chunks: List[Dict[str, Any]], embeddings: List[List[float]]):self.chunks.extend(chunks)self.embeddings.extend(embeddings)# --- 新增:构建 BM25 ---# 使用 jieba 精确分词self.tokenized_chunks = [list(jieba.cut_for_search(c['content']))  # 搜索引擎模式,速度快for c in self.chunks]self.bm25 = BM25Okapi(self.tokenized_chunks)def search(self, query_embedding: List[float], top_k: int = 3) -> List[Dict[str, Any]]:from numpy import dotfrom numpy.linalg import normimport numpy as npif not self.embeddings:return []emb_matrix = np.array(self.embeddings)query_emb = np.array(query_embedding)sims = emb_matrix @ query_emb / (norm(emb_matrix, axis=1) * norm(query_emb) + 1e-8)idxs = sims.argsort()[::-1][:top_k]return [self.chunks[i] for i in idxs]# --- 新增:bm25检索 ---def search_bm25(self, query: str, top_k: int = 3) -> List[Dict[str, Any]]:if not self.bm25:return []# 同样用 jieba 分词tokens = list(jieba.cut_for_search(query))scores = self.bm25.get_scores(tokens)idxs = scores.argsort()[::-1][:top_k]return [self.chunks[i] for i in idxs]

step3:在 SimpleRAG 类中新增混合检索接口

class SimpleRAG:def __init__(self, chunk_json_path: str, model_path: str = None, batch_size: int = 32):self.loader = PageChunkLoader(chunk_json_path)self.embedding_model = EmbeddingModel(batch_size=batch_size)self.vector_store = SimpleVectorStore()self.memory = ConversationBufferMemory(return_messages=True)def search_hybrid(self, question: str, top_k_vec: int = 10, top_k_bm25: int = 10) -> List[Dict[str, Any]]:"""混合检索:向量 + BM25,各取 top_k,合并去重后返回"""# 向量检索q_emb = self.embedding_model.embed_text(question)vec_results = self.vector_store.search(q_emb, top_k=top_k_vec)# BM25 检索bm25_results = self.vector_store.search_bm25(question, top_k=top_k_bm25)# 合并去重(保持顺序)seen = set()merged = []for chunk in vec_results + bm25_results:cid = (chunk['metadata']['file_name'], chunk['metadata']['page'], chunk['content'])if cid not in seen:seen.add(cid)merged.append(chunk)return merged

step4:函数应用,修改原来search方法为混合检索

# chunks = self.vector_store.search(q_emb, top_k)
# 2. 混合检索
chunks = self.search_hybrid(rewritten_question)


5、反思重写:

我们甚至可以考虑让RAG系统拥有自我修正的能力。

具体来说,就是让系统在检索一次之后,能自己判断一下找到的上下文够不够回答问题。

如果不够,它可以自己生成一个新的、更具体的查询语句,再次进行检索,把两次的结果合在一起再生成答案。

这会让整个问答过程更动态一些。

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

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

相关文章

09--解密栈与队列:数据结构核心原理

1. 栈 1.1. 栈的简介 栈 是一种 特殊的线性表&#xff0c;具有数据 先进后出 特点。 注意&#xff1a; stack本身 不支持迭代器操作 主要原因是因为stack不支持数据的随机访问&#xff0c;必须保证数据先进后出的特点。stack在CPP库中实现为一种 容器适配器 所谓容器适配器&a…

打造专属 React 脚手架:从 0 到 1 开发 CLI 工具

前言: 在前端开发中&#xff0c;重复搭建项目环境是个低效的事儿。要是团队技术栈固定&#xff08;比如 React AntD Zustand TS &#xff09;&#xff0c;每次从零开始配路由、状态管理、UI 组件&#xff0c;既耗时又容易出错。这时候&#xff0c;自定义 CLI 脚手架 就派上…

Python day43

浙大疏锦行 Python day43 import torch import numpy as np import pandas as pd import torchvision import torchvision.transforms as transforms import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torch.utils.data import Da…

python基于Hadoop的超市数据分析系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 摘要&…

如何用 COLMAP 制作 Blender 格式的数据集

如何用 COLMAP 制作 Blender 格式的数据集并划分出 transforms_train.json、transforms_val.json 和 transforms_test.json。 一、什么是 Blender 格式数据集? Blender 格式数据集是 Nerf 和 Nerfstudio 常用的输入格式,其核心是包含了相机内外参的 JSON 文件,一般命名为:…

[GESP202309 六级] 2023年9月GESP C++六级上机题题解,附带讲解视频!

本文为GESP 2023年9月 六级的上机题目详细题解和讲解视频&#xff0c;觉得有帮助或者写的不错可以点个赞。 题目一讲解视频 GESP2023年9月六级上机题一题目二讲解视频 题目一:小羊买饮料 B3873 [GESP202309 六级] 小杨买饮料 - 洛谷 题目大意: 现在超市一共有n种饮料&#…

linux 操作ppt

目录 方法1&#xff1a;用 libreoffice 打开PPT文件 播放脚本&#xff1a; 方法2&#xff1a;用 python-pptx 创建和编辑PPT 方法3&#xff1a;其他方法 在Linux中&#xff0c;可以使用Python通过python-pptx库来创建和编辑PPT文件&#xff0c;但直接播放PPT文件需要借助其…

元数据管理与数据治理平台:Apache Atlas 基本搜索 Basic Search

文中内容仅限技术学习与代码实践参考&#xff0c;市场存在不确定性&#xff0c;技术分析需谨慎验证&#xff0c;不构成任何投资建议。 Apache Atlas 框架是一套可扩展的核心基础治理服务&#xff0c;使企业能够有效、高效地满足 Hadoop 中的合规性要求&#xff0c;并支持与整个…

LangChain4J-(1)-Hello World

一、LangChain4J是什么&#xff1f; LangChain4J 是一个专为 Java 生态系统设计的开源框架&#xff0c;用于简化与大语言模型&#xff08;LLM&#xff0c;如 OpenAI 的 GPT 系列、Google 的 Gemini、Anthropic 的 Claude 等&#xff09;的集成和交互。它借鉴了 Python 生态中 L…

HTTPS应用层协议-中间攻击人

HTTPS应用层协议-中间攻击人 • Man-in-the-MiddleAttack&#xff0c;简称“MITM 攻击” 确实&#xff0c;在方案 2/3/4 中&#xff0c;客户端获取到公钥 S 之后&#xff0c;对客户端形成的对称秘钥 X 用服务端给客户端的公钥 S 进行加密&#xff0c;中间人即使窃取到了数据&am…

利用 Makefile 高效启动 VIVADO 软件:深入解析与实践

利用 Makefile 高效启动 VIVADO 软件&#xff1a;深入解析与实践 系列文章目录 1、VMware Workstation Pro安装指南&#xff1a;详细步骤与配置选项说明 2、VMware 下 Ubuntu 操作系统下载与安装指南 3.基于 Ubuntu 的 Linux 系统中 Vivado 2020.1 下载安装教程 文章目录利用 …

[前端算法]排序算法

默认情况下&#xff0c;sort() 会将元素转换为字符串&#xff0c;然后按照 Unicode 编码的顺序进行排序&#xff1a; const fruits [apple, banana, cherry, date]; fruits.sort(); console.log(fruits); // 输出: ["apple", "banana", "cherry"…

C#标签批量打印程序开发

C#标签批量打印程序开发&#xff08;集成Bartender解决方案&#xff09;一、系统架构设计 1. 核心模块划分 public class LabelPrintingSystem {private IDataLoader _dataLoader; // 数据加载器private ITemplateEngine _templateEngine; // 模板引擎private IPrintControl…

ECC的原理、背景、工作机制和数学基础

ECC的原理、背景、工作机制和数学基础摘要&#xff1a;本文首先详细介绍ECC&#xff08;Error-Correcting Code&#xff0c;纠错码&#xff09;的原理&#xff0c;包括背景、工作机制和数学基础。然后&#xff0c;解释ECC在SRAM&#xff08;Static Random-Access Memory&#x…

计算机网络2-2:物理层下面的传输媒体

目录 导引型传输媒体 同轴电缆 双绞线 光纤 电力线 非导引型传输媒体 无线电波 微波 红外线 可见光 无线电频谱管理机构 导引型传输媒体 同轴电缆 双绞线 光纤 光在光纤中传播的基本原理 电力线 非导引型传输媒体 无线电波 微波 红外线 可见光 LiFi(可见光通信) …

Dify 从入门到精通(第 32/100 篇):Dify 的日志分析与监控

Dify 从入门到精通&#xff08;第 32/100 篇&#xff09;&#xff1a;Dify 的日志分析与监控 Dify 入门到精通系列文章目录 第一篇《Dify 究竟是什么&#xff1f;真能开启低代码 AI 应用开发的未来&#xff1f;》介绍了 Dify 的定位与优势第二篇《Dify 的核心组件&#xff1a…

【IntelliJ IDEA】修改堆内存

idea卡顿&#xff0c;鼠标漂移修改idea文件打开 idea 安装路径&#xff0c;【bin】目录下【idea64.exe.vmoptions】文件修改【-Xms】最小内存【-Xmx】最大内存-Xms2048m -Xmx9216midea更改内存设置工具栏帮助更改内存设置设置堆大小上限为 文件 设置的最大内存保存并重启Leslie…

Docker与Docker Compose:容器世界的“单兵作战”与“军团指挥官”

在容器化技术的浪潮中&#xff0c;Docker和Docker Compose如同“双子星”&#xff0c;一个专注于单兵作战&#xff0c;一个擅长军团指挥。它们看似相似&#xff0c;却各司其职。对于开发者来说&#xff0c;理解它们的区别不仅能让代码部署事半功倍&#xff0c;更能避免踩坑。本…

进阶向:Python编写自动化邮件发送程序

Python编写自动化邮件发送程序&#xff1a;从零开始详解在数字化时代&#xff0c;自动化邮件发送功能已成为企业和个人提升工作效率的重要工具。据统计&#xff0c;全球每天发送的商业邮件超过30亿封&#xff0c;其中约40%是通过自动化系统发送的。这种功能被广泛应用于多种场景…

ChatGpt 5系列文章1——编码与智能体

人工智能技术正在以惊人的速度发展&#xff0c;重新定义着开发人员的工作方式。2025年8月&#xff0c;OpenAI正式发布了面向开发人员的GPT-5 一、GPT-5的编码能力突破 GPT-5在关键编码基准测试中创造了行业新纪录(SOTA)&#xff0c;在SWE-bench Verified测试中得分74.9%&…